]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
D3: execve argv/envp stack + errno
authorTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 01:51:33 +0000 (22:51 -0300)
committerTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 01:51:33 +0000 (22:51 -0300)
Copy argv/envp from user memory and build a minimal initial user stack in the new address space during execve(). Improve errno reporting (EFAULT/ENOENT/ENOMEM/EINVAL).

src/kernel/syscall.c

index 0baf2968aebb4972dfffa947dc5bbf9e279e1cae..c71e1c85eae3243e7bcfba8d4c9d8fd1e426ce84 100644 (file)
 static int fd_alloc(struct file* f);
 static int fd_close(int fd);
 
+static int execve_copy_user_str(char* out, size_t out_sz, const char* user_s) {
+    if (!out || out_sz == 0 || !user_s) return -EFAULT;
+    for (size_t i = 0; i < out_sz; i++) {
+        if (copy_from_user(&out[i], &user_s[i], 1) < 0) return -EFAULT;
+        if (out[i] == 0) return 0;
+    }
+    out[out_sz - 1] = 0;
+    return 0;
+}
+
+static int execve_copy_user_ptr(const void* const* user_p, uintptr_t* out) {
+    if (!out) return -EFAULT;
+    if (!user_p) {
+        *out = 0;
+        return 0;
+    }
+    uintptr_t tmp = 0;
+    if (copy_from_user(&tmp, user_p, sizeof(tmp)) < 0) return -EFAULT;
+    *out = tmp;
+    return 0;
+}
+
 static int syscall_fork_impl(struct registers* regs) {
     if (!regs) return -EINVAL;
     if (!current_process) return -EINVAL;
@@ -306,10 +328,14 @@ static int syscall_dup_impl(int oldfd) {
 }
 
 static int syscall_execve_impl(struct registers* regs, const char* user_path, const char* const* user_argv, const char* const* user_envp) {
-    (void)user_argv;
-    (void)user_envp;
     if (!regs || !user_path) return -EFAULT;
 
+    enum {
+        EXECVE_MAX_ARGC = 32,
+        EXECVE_MAX_ENVC = 32,
+        EXECVE_MAX_STR  = 128,
+    };
+
     char path[128];
     for (size_t i = 0; i < sizeof(path); i++) {
         if (copy_from_user(&path[i], &user_path[i], 1) < 0) {
@@ -322,11 +348,45 @@ static int syscall_execve_impl(struct registers* regs, const char* user_path, co
         }
     }
 
+    // Snapshot argv/envp into kernel buffers (before switching addr_space).
+    char kargv[EXECVE_MAX_ARGC][EXECVE_MAX_STR];
+    char kenvp[EXECVE_MAX_ENVC][EXECVE_MAX_STR];
+    int argc = 0;
+    int envc = 0;
+
+    if (user_argv) {
+        for (int i = 0; i < EXECVE_MAX_ARGC; i++) {
+            uintptr_t up = 0;
+            int rc = execve_copy_user_ptr((const void* const*)&user_argv[i], &up);
+            if (rc < 0) return rc;
+            if (up == 0) break;
+            rc = execve_copy_user_str(kargv[i], sizeof(kargv[i]), (const char*)up);
+            if (rc < 0) return rc;
+            argc++;
+        }
+    }
+
+    if (user_envp) {
+        for (int i = 0; i < EXECVE_MAX_ENVC; i++) {
+            uintptr_t up = 0;
+            int rc = execve_copy_user_ptr((const void* const*)&user_envp[i], &up);
+            if (rc < 0) return rc;
+            if (up == 0) break;
+            rc = execve_copy_user_str(kenvp[i], sizeof(kenvp[i]), (const char*)up);
+            if (rc < 0) return rc;
+            envc++;
+        }
+    }
+
+    // Distinguish ENOENT early.
+    fs_node_t* node = vfs_lookup(path);
+    if (!node) return -ENOENT;
+
     uintptr_t entry = 0;
     uintptr_t user_sp = 0;
     uintptr_t new_as = 0;
     if (elf32_load_user_from_initrd(path, &entry, &user_sp, &new_as) != 0) {
-        return -ENOENT;
+        return -EINVAL;
     }
 
     uintptr_t old_as = current_process ? current_process->addr_space : 0;
@@ -338,12 +398,55 @@ static int syscall_execve_impl(struct registers* regs, const char* user_path, co
     current_process->addr_space = new_as;
     vmm_as_activate(new_as);
 
+    // Build a minimal initial user stack: argc, argv pointers, envp pointers, strings.
+    // The loader returns a fresh stack top (user_sp). We'll pack strings below it.
+    uintptr_t sp = user_sp;
+    sp &= ~(uintptr_t)0xF;
+
+    uintptr_t argv_ptrs_va[EXECVE_MAX_ARGC + 1];
+    uintptr_t envp_ptrs_va[EXECVE_MAX_ENVC + 1];
+
+    for (int i = envc - 1; i >= 0; i--) {
+        size_t len = strlen(kenvp[i]) + 1;
+        sp -= len;
+        memcpy((void*)sp, kenvp[i], len);
+        envp_ptrs_va[i] = sp;
+    }
+    envp_ptrs_va[envc] = 0;
+
+    for (int i = argc - 1; i >= 0; i--) {
+        size_t len = strlen(kargv[i]) + 1;
+        sp -= len;
+        memcpy((void*)sp, kargv[i], len);
+        argv_ptrs_va[i] = sp;
+    }
+    argv_ptrs_va[argc] = 0;
+
+    sp &= ~(uintptr_t)0xF;
+
+    // Push envp[] pointers
+    sp -= (uintptr_t)(sizeof(uintptr_t) * (envc + 1));
+    memcpy((void*)sp, envp_ptrs_va, sizeof(uintptr_t) * (envc + 1));
+    uintptr_t envp_va = sp;
+
+    // Push argv[] pointers
+    sp -= (uintptr_t)(sizeof(uintptr_t) * (argc + 1));
+    memcpy((void*)sp, argv_ptrs_va, sizeof(uintptr_t) * (argc + 1));
+    uintptr_t argv_va = sp;
+
+    // Push argc
+    sp -= sizeof(uint32_t);
+    *(uint32_t*)sp = (uint32_t)argc;
+
+    (void)argv_va;
+    (void)envp_va;
+
     if (old_as && old_as != new_as) {
         vmm_as_destroy(old_as);
     }
 
     regs->eip = (uint32_t)entry;
-    regs->useresp = (uint32_t)user_sp;
+    regs->useresp = (uint32_t)sp;
     regs->eax = 0;
     return 0;
 }