From e92fbe3e6922e83f86deef3a8f38d5d2be142e09 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Thu, 25 Dec 2025 23:33:03 +0800 Subject: [PATCH] Correct ELF alignment settings --- .../process/program_loader/elf/load_elf.rs | 22 +++-- test/src/apps/execve/execve_err.c | 86 +++++++++++++++++-- 2 files changed, 92 insertions(+), 16 deletions(-) diff --git a/kernel/src/process/program_loader/elf/load_elf.rs b/kernel/src/process/program_loader/elf/load_elf.rs index d68dfffc1..df298ee5e 100644 --- a/kernel/src/process/program_loader/elf/load_elf.rs +++ b/kernel/src/process/program_loader/elf/load_elf.rs @@ -21,7 +21,7 @@ use crate::{ }, vm::{ perms::VmPerms, - vmar::{VMAR_LOWEST_ADDR, Vmar}, + vmar::{VMAR_CAP_ADDR, VMAR_LOWEST_ADDR, Vmar}, vmo::Vmo, }, }; @@ -205,24 +205,28 @@ fn map_segment_vmos( let map_range = if elf.is_shared_object() { // Relocatable object. + let align = elf.max_load_align(); + + // Given that `elf_va_range` is guaranteed to be below `VMAR_CAP_ADDR`, as long as + // `VMAR_CAP_ADDR * 2` does not overflow, the following `align_up(align)` cannot overflow + // either. + const { assert!(VMAR_CAP_ADDR.checked_mul(2).is_some()) }; + // Allocate a continuous range of virtual memory for all segments in advance. // // All segments in the ELF program must be mapped to a continuous VM range to // ensure the relative offset of each segment not changed. let elf_va_range_aligned = - elf_va_range.start.align_down(PAGE_SIZE)..elf_va_range.end.align_up(PAGE_SIZE); + elf_va_range.start.align_down(align)..elf_va_range.end.align_up(align); let map_size = elf_va_range_aligned.len(); - let vmar_map_options = vmar - .new_map(map_size, VmPerms::empty())? - .align(elf.max_load_align()) - .handle_page_faults_around(); + let vmar_map_options = vmar.new_map(map_size, VmPerms::empty())?.align(align); let aligned_range = vmar_map_options.build().map(|addr| addr..addr + map_size)?; - let start_in_page_offset = elf_va_range.start - elf_va_range_aligned.start; - let end_in_page_offset = elf_va_range_aligned.end - elf_va_range.end; + let start_offset = elf_va_range.start - elf_va_range_aligned.start; + let end_offset = elf_va_range_aligned.end - elf_va_range.end; - aligned_range.start + start_in_page_offset..aligned_range.end - end_in_page_offset + aligned_range.start + start_offset..aligned_range.end - end_offset } else { // Not relocatable object. Map as-is. diff --git a/test/src/apps/execve/execve_err.c b/test/src/apps/execve/execve_err.c index 753684fb6..0fa6464f9 100644 --- a/test/src/apps/execve/execve_err.c +++ b/test/src/apps/execve/execve_err.c @@ -24,7 +24,7 @@ static struct custom_elf elf; #define UD2_INSTR \ "\x0f\x0b" // "ud2" in x86-64. TODO: Support other architectures. -FN_SETUP(init) +FN_SETUP(init_exec) { elf.ehdr.e_ident[EI_MAG0] = ELFMAG0; elf.ehdr.e_ident[EI_MAG1] = ELFMAG1; @@ -71,23 +71,30 @@ static int do_execve(void) #pragma GCC diagnostic pop } -FN_TEST(good) +static int do_execve_good(void) { pid_t pid; int status; - // First of all, verify that `elf` is a good ELF. - memcpy(elf.buf, UD2_INSTR, sizeof(UD2_INSTR)); - pid = TEST_SUCC(fork()); + pid = CHECK(fork()); if (pid == 0) { CHECK(do_execve()); exit(EXIT_FAILURE); } - TEST_RES(wait(&status), _ret == pid && WIFSIGNALED(status) && - WTERMSIG(status) == SIGILL); + CHECK_WITH(wait(&status), _ret == pid); + if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGILL) + return -1; + + return 0; +} + +FN_TEST(good_exec) +{ + // First of all, verify that `elf` is a good ELF. + TEST_RES(do_execve_good(), _ret == 0); } END_TEST() @@ -418,6 +425,71 @@ FN_TEST(filesz_larger_than_memsz) } END_TEST() +// ========================== +// Below are tests for ET_DYN +// ========================== + +FN_SETUP(init_dyn) +{ + elf.ehdr.e_type = ET_DYN; +} +END_SETUP() + +FN_TEST(good_dyn) +{ + // First of all, verify that `elf` is a good ELF. + TEST_RES(do_execve_good(), _ret == 0); +} +END_TEST() + +FN_TEST(bad_align) +{ + long old; + + old = elf.phdr[0].p_align; + + // 2048 is smaller than PAGE_SIZE. + elf.phdr[0].p_align = 2048; + TEST_RES(do_execve_good(), _ret == 0); + + // 2047 is not a power of two. + elf.phdr[0].p_align = 2047; + TEST_RES(do_execve_good(), _ret == 0); + + elf.phdr[0].p_align = old; +} +END_TEST() + +FN_TEST(large_align) +{ + long old; + + old = elf.phdr[0].p_align; + + elf.phdr[0].p_align = 1ul << 21; + TEST_RES(do_execve_good(), _ret == 0); + + elf.phdr[0].p_align = 1ul << 42; + TEST_RES(do_execve_good(), _ret == 0); + + elf.phdr[0].p_align = 1ul << 63; + // FIXME: p_align exceeds the size of the user address space. + // What does this mean? As far as Linux is concerned, it tries + // to map to a zero address due to [1], [2], and [3]. This + // does not seem to make much sense. + // [1]: https://elixir.bootlin.com/linux/v6.19-rc2/source/fs/binfmt_elf.c#L1172 + // [2]: https://elixir.bootlin.com/linux/v6.19-rc2/source/fs/binfmt_elf.c#L1185 + // [3]: https://elixir.bootlin.com/linux/v6.19-rc2/source/fs/binfmt_elf.c#L1188 +#ifdef __asterinas__ + TEST_ERRNO(do_execve_fatal(), ENOMEM); +#else + TEST_ERRNO(do_execve_fatal(), EPERM); +#endif + + elf.phdr[0].p_align = old; +} +END_TEST() + FN_SETUP(cleanup) { CHECK(unlink(EXE_PATH));