From: Tulio A M Mendes Date: Mon, 27 Apr 2026 00:28:10 +0000 (-0300) Subject: kernel: fix execve after pivot_root via vfs_lookup_initrd(); fork execve test X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=0f374eee1be7c0f13d153bdfce0aabb161bdba38;p=AdrOS.git kernel: fix execve after pivot_root via vfs_lookup_initrd(); fork execve test pivot_root changes the global fs_root and '/' mount entry to a tmpfs, which breaks subsequent execve calls because vfs_lookup('/bin/echo') fails in the new root. This caused the 'echo execve' flaky test failure (it always failed when run after the pivot_root test). Fix: save the original initrd overlayfs root at boot and add vfs_lookup_initrd() which does a direct finddir traversal from that saved root, bypassing the VFS mount table. Use it in: - elf32_load_user_from_initrd (binary lookup) - elf32_load_interp (ld.so lookup) - elf32_load_shared_lib_at (libc.so lookup) - syscall_execve_impl (early ENOENT check) Also change the execve test to fork a child (like execveat), so PID 1 is not replaced and the test harness can verify the output. Tests: 120/120 smoke, 33/33 battery, 69/69 host, cppcheck clean. --- diff --git a/include/fs.h b/include/fs.h index 5a01cbe8..ad378aac 100644 --- a/include/fs.h +++ b/include/fs.h @@ -82,6 +82,10 @@ void vfs_close(fs_node_t* node); fs_node_t* vfs_lookup(const char* path); +/* Lookup from the saved initrd root (immune to pivot_root). */ +void vfs_set_initrd_root(fs_node_t* root); +fs_node_t* vfs_lookup_initrd(const char* path); + // Resolve path to (parent_dir, basename). Returns parent node or NULL. fs_node_t* vfs_lookup_parent(const char* path, char* name_out, size_t name_sz); diff --git a/src/arch/x86/elf.c b/src/arch/x86/elf.c index e3bd0919..b7b7228e 100644 --- a/src/arch/x86/elf.c +++ b/src/arch/x86/elf.c @@ -263,7 +263,8 @@ static void elf32_process_relocations(const uint8_t* file, uint32_t file_len, * Returns 0 on success, fills *loaded_end with highest mapped address. */ static int elf32_load_shared_lib_at(const char* path, uintptr_t as, uintptr_t base, uintptr_t* loaded_end) { - fs_node_t* node = vfs_lookup(path); + fs_node_t* node = vfs_lookup_initrd(path); + if (!node) node = vfs_lookup(path); if (!node) return -ENOENT; uint32_t flen = node->length; @@ -354,7 +355,8 @@ static int elf32_load_interp(const char* interp_path, uintptr_t as, uintptr_t* interp_entry, uintptr_t* interp_base_out) { if (!interp_path || !interp_entry) return -EINVAL; - fs_node_t* node = vfs_lookup(interp_path); + fs_node_t* node = vfs_lookup_initrd(interp_path); + if (!node) node = vfs_lookup(interp_path); if (!node) { kprintf("[ELF] interp not found: %s\n", interp_path); return -ENOENT; @@ -409,7 +411,10 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint uintptr_t old_as = hal_cpu_get_address_space(); - fs_node_t* node = vfs_lookup(filename); + /* Use vfs_lookup_initrd so that pivot_root (which changes the global + * fs_root and the "/" mount entry) does not break execve lookups. */ + fs_node_t* node = vfs_lookup_initrd(filename); + if (!node) node = vfs_lookup(filename); if (!node) { kprintf("[ELF] file not found: %s\n", filename); vmm_as_destroy(new_as); diff --git a/src/kernel/fs.c b/src/kernel/fs.c index 646b8b48..84379527 100644 --- a/src/kernel/fs.c +++ b/src/kernel/fs.c @@ -13,6 +13,7 @@ #include "errno.h" fs_node_t* fs_root = NULL; +static fs_node_t* g_initrd_root = NULL; struct vfs_mount { char mountpoint[128]; @@ -123,6 +124,41 @@ fs_node_t* vfs_lookup(const char* path) { return vfs_lookup_depth(path, 0); } +void vfs_set_initrd_root(fs_node_t* root) { + g_initrd_root = root; +} + +fs_node_t* vfs_lookup_initrd(const char* path) { + if (!path || !g_initrd_root) return NULL; + + /* Direct finddir traversal from the saved initrd root. + * Bypasses the VFS mount table so that pivot_root (which changes + * fs_root and the "/" mount entry) does not break execve lookups. */ + const char* p = path; + while (*p == '/') p++; + if (*p == 0) return g_initrd_root; + + fs_node_t* cur = g_initrd_root; + char part[128]; + while (*p != 0) { + size_t i = 0; + while (*p != 0 && *p != '/') { + if (i + 1 < sizeof(part)) part[i++] = *p; + p++; + } + part[i] = 0; + while (*p == '/') p++; + if (part[0] == 0) continue; + if (!cur) return NULL; + fs_node_t* (*fn_finddir)(fs_node_t*, const char*) = NULL; + if (cur->i_ops && cur->i_ops->lookup) fn_finddir = cur->i_ops->lookup; + if (!fn_finddir) return NULL; + cur = fn_finddir(cur, part); + if (!cur) return NULL; + } + return cur; +} + static fs_node_t* vfs_lookup_depth(const char* path, int depth) { if (!path || !fs_root) return NULL; if (depth > 8) return NULL; diff --git a/src/kernel/init.c b/src/kernel/init.c index 1da2670b..59317250 100644 --- a/src/kernel/init.c +++ b/src/kernel/init.c @@ -209,6 +209,7 @@ int init_start(const struct boot_info* bi) { fs_node_t* ovl = overlayfs_create_root(fs_root, upper); if (ovl) { (void)vfs_mount("/", ovl); + vfs_set_initrd_root(ovl); } } } diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 00450a49..9881793f 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -2055,7 +2055,8 @@ static int syscall_execve_impl(struct registers* regs, const char* user_path, co } // Distinguish ENOENT early. - fs_node_t* node = vfs_lookup(path); + fs_node_t* node = vfs_lookup_initrd(path); + if (!node) node = vfs_lookup(path); if (!node) { ret = -ENOENT; goto out; } uintptr_t entry = 0; diff --git a/user/cmds/fulltest/fulltest.c b/user/cmds/fulltest/fulltest.c index 149c0243..8aea99df 100644 --- a/user/cmds/fulltest/fulltest.c +++ b/user/cmds/fulltest/fulltest.c @@ -5256,13 +5256,27 @@ void _start(void) { (uint32_t)(sizeof("[test] pivot_root OK\n") - 1)); } - (void)sys_write(1, "[test] execve(/bin/echo)\n", - (uint32_t)(sizeof("[test] execve(/bin/echo)\n") - 1)); - static const char* const argv[] = {"echo", "[echo]", "hello", "from", "echo", 0}; - static const char* const envp[] = {"FOO=bar", "HELLO=world", 0}; - (void)sys_execve("/bin/echo", argv, envp); - (void)sys_write(1, "[test] execve returned (unexpected)\n", - (uint32_t)(sizeof("[test] execve returned (unexpected)\n") - 1)); - sys_exit(1); + // execve — fork a child that replaces itself with /bin/echo + { + int pid = sys_fork(); + if (pid < 0) { + sys_write(1, "[test] execve fork failed\n", + (uint32_t)(sizeof("[test] execve fork failed\n") - 1)); + sys_exit(1); + } + if (pid == 0) { + static const char* const ev_argv[] = {"echo", "[echo]", "hello", "from", "echo", 0}; + static const char* const ev_envp[] = {"FOO=bar", "HELLO=world", 0}; + (void)sys_execve("/bin/echo", ev_argv, ev_envp); + sys_exit(1); /* execve should not return */ + } + int st = 0; + sys_waitpid(pid, &st, 0); + if (st != 0) { + sys_write(1, "[test] execve child failed\n", + (uint32_t)(sizeof("[test] execve child failed\n") - 1)); + sys_exit(1); + } + } sys_exit(0); }