Implement SYSCALL_SIGRETURN and userland signal trampoline/sigframe so handlers can return safely.
Fix x86 signal delivery stack layout to avoid clobbering sigframe/trampoline.
Fix execve heap corruption by avoiding large on-stack argv/envp buffers and adding cleanup.
Add init.elf smoke test for sigreturn.
#define ENOENT 2
#define EIO 5
#define EINTR 4
+#define E2BIG 7
#define EBADF 9
#define ECHILD 10
#define EFAULT 14
SYSCALL_SIGACTION = 25,
SYSCALL_SIGPROCMASK = 26,
+ SYSCALL_SIGRETURN = 27,
};
#endif
#include "process.h"
#include "spinlock.h"
#include "uaccess.h"
+#include "syscall.h"
#include <stddef.h>
#define IDT_ENTRIES 256
+static const uint32_t SIGFRAME_MAGIC = 0x53494746U; // 'SIGF'
+
+struct sigframe {
+ uint32_t magic;
+ struct registers saved;
+};
+
struct idt_entry idt[IDT_ENTRIES];
struct idt_ptr idtp;
return;
}
- uint32_t frame[2];
- frame[0] = regs->eip;
- frame[1] = (uint32_t)sig;
+ // Build a sigframe + a tiny user trampoline that calls SYSCALL_SIGRETURN.
+ // Stack layout at handler entry (regs->useresp):
+ // [esp+0] return address -> trampoline
+ // [esp+4] int sig
+ // Below that: trampoline code bytes, below that: sigframe.
+
+ struct sigframe f;
+ f.magic = SIGFRAME_MAGIC;
+ f.saved = *regs;
+
+ const uint32_t tramp_size = 14U;
+ const uint32_t base = regs->useresp - (8U + tramp_size + (uint32_t)sizeof(f));
+ const uint32_t retaddr_slot = base;
+ const uint32_t tramp_addr = base + 8U;
+ const uint32_t sigframe_addr = tramp_addr + tramp_size;
+
+ // Trampoline bytes:
+ // mov eax, SYSCALL_SIGRETURN
+ // mov ebx, <sigframe_addr>
+ // int 0x80
+ // jmp .
+ uint8_t tramp[14];
+ tramp[0] = 0xB8;
+ {
+ const uint32_t no = (uint32_t)SYSCALL_SIGRETURN;
+ tramp[1] = (uint8_t)(no & 0xFFU);
+ tramp[2] = (uint8_t)((no >> 8) & 0xFFU);
+ tramp[3] = (uint8_t)((no >> 16) & 0xFFU);
+ tramp[4] = (uint8_t)((no >> 24) & 0xFFU);
+ }
+ tramp[5] = 0xBB;
+ // tramp[6..9] patched with sigframe address below
+ tramp[10] = 0xCD;
+ tramp[11] = 0x80;
+ tramp[12] = 0xEB;
+ tramp[13] = 0xFE;
+
+ tramp[6] = (uint8_t)(sigframe_addr & 0xFFU);
+ tramp[7] = (uint8_t)((sigframe_addr >> 8) & 0xFFU);
+ tramp[8] = (uint8_t)((sigframe_addr >> 16) & 0xFFU);
+ tramp[9] = (uint8_t)((sigframe_addr >> 24) & 0xFFU);
+
+ if (copy_to_user((void*)(uintptr_t)sigframe_addr, &f, sizeof(f)) < 0 ||
+ copy_to_user((void*)(uintptr_t)tramp_addr, tramp, sizeof(tramp)) < 0) {
+ const int SIG_SEGV = 11;
+ process_exit_notify(128 + SIG_SEGV);
+ __asm__ volatile("sti");
+ schedule();
+ for (;;) __asm__ volatile("hlt");
+ }
- const uint32_t new_esp = regs->useresp - (uint32_t)sizeof(frame);
- if (copy_to_user((void*)(uintptr_t)new_esp, frame, sizeof(frame)) < 0) {
+ uint32_t callframe[2];
+ callframe[0] = tramp_addr;
+ callframe[1] = (uint32_t)sig;
+ if (copy_to_user((void*)(uintptr_t)retaddr_slot, callframe, sizeof(callframe)) < 0) {
const int SIG_SEGV = 11;
process_exit_notify(128 + SIG_SEGV);
__asm__ volatile("sti");
for (;;) __asm__ volatile("hlt");
}
- regs->useresp = new_esp;
+ regs->useresp = retaddr_slot;
regs->eip = (uint32_t)h;
}
#include "syscall.h"
#include "idt.h"
#include "fs.h"
-#include "heap.h"
-#include "tty.h"
#include "process.h"
-#include "uart_console.h"
+#include "spinlock.h"
#include "uaccess.h"
+#include "uart_console.h"
#include "utils.h"
+#include "heap.h"
+#include "tty.h"
+
#include "errno.h"
#include "elf.h"
#include "stat.h"
#include <stddef.h>
+#if defined(__i386__)
+static const uint32_t SIGFRAME_MAGIC = 0x53494746U; // 'SIGF'
+struct sigframe {
+ uint32_t magic;
+ struct registers saved;
+};
+#endif
+
static int fd_alloc(struct file* f);
static int fd_close(int fd);
static struct file* fd_get(int fd);
}
// 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];
+ char (*kargv)[EXECVE_MAX_STR] = (char(*)[EXECVE_MAX_STR])kmalloc((size_t)EXECVE_MAX_ARGC * (size_t)EXECVE_MAX_STR);
+ char (*kenvp)[EXECVE_MAX_STR] = (char(*)[EXECVE_MAX_STR])kmalloc((size_t)EXECVE_MAX_ENVC * (size_t)EXECVE_MAX_STR);
int argc = 0;
int envc = 0;
+ int ret = 0;
+
+ if (!kargv || !kenvp) {
+ ret = -ENOMEM;
+ goto out;
+ }
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 (rc < 0) { ret = rc; goto out; }
if (up == 0) break;
rc = execve_copy_user_str(kargv[i], sizeof(kargv[i]), (const char*)up);
- if (rc < 0) return rc;
+ if (rc < 0) { ret = rc; goto out; }
argc++;
}
}
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 (rc < 0) { ret = rc; goto out; }
if (up == 0) break;
rc = execve_copy_user_str(kenvp[i], sizeof(kenvp[i]), (const char*)up);
- if (rc < 0) return rc;
+ if (rc < 0) { ret = rc; goto out; }
envc++;
}
}
// Distinguish ENOENT early.
fs_node_t* node = vfs_lookup(path);
- if (!node) return -ENOENT;
+ if (!node) { ret = -ENOENT; goto out; }
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 -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
+ const size_t user_stack_size = 0x1000U;
+
+ if ((size_t)((argc + 1) + (envc + 1)) * sizeof(uintptr_t) + (size_t)argc * EXECVE_MAX_STR + (size_t)envc * EXECVE_MAX_STR + 64U > user_stack_size) { vmm_as_destroy(new_as); ret = -E2BIG; goto out; }
uintptr_t old_as = current_process ? current_process->addr_space : 0;
if (!current_process) {
vmm_as_destroy(new_as);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
current_process->addr_space = new_as;
regs->eip = (uint32_t)entry;
regs->useresp = (uint32_t)sp;
regs->eax = 0;
- return 0;
+ ret = 0;
+ goto out;
+
+out:
+ if (kargv) kfree(kargv);
+ if (kenvp) kfree(kenvp);
+ return ret;
}
static int syscall_dup2_impl(int oldfd, int newfd) {
return -EINVAL;
}
+static int syscall_sigreturn_impl(struct registers* regs, const struct sigframe* user_frame) {
+ if (!regs) return -EINVAL;
+ if (!current_process) return -EINVAL;
+ if ((regs->cs & 3U) != 3U) return -EPERM;
+ if (!user_frame) return -EFAULT;
+
+ if (user_range_ok(user_frame, sizeof(*user_frame)) == 0) { return -EFAULT; }
+
+ struct sigframe f;
+ if (copy_from_user(&f, user_frame, sizeof(f)) < 0) return -EFAULT;
+ if (f.magic != SIGFRAME_MAGIC) { return -EINVAL; }
+
+ if ((f.saved.cs & 3U) != 3U) return -EPERM;
+ if ((f.saved.ss & 3U) != 3U) return -EPERM;
+
+ // Restore the full saved trapframe. The interrupt stub will pop these regs and iret.
+ *regs = f.saved;
+ return 0;
+}
+
static void syscall_handler(struct registers* regs) {
uint32_t syscall_no = regs->eax;
return;
}
+ if (syscall_no == SYSCALL_SIGRETURN) {
+ const struct sigframe* user_frame = (const struct sigframe*)regs->ebx;
+ regs->eax = (uint32_t)syscall_sigreturn_impl(regs, user_frame);
+ return;
+ }
+
regs->eax = (uint32_t)-ENOSYS;
}
SYSCALL_SIGACTION = 25,
SYSCALL_SIGPROCMASK = 26,
+ SYSCALL_SIGRETURN = 27,
};
enum {
}
static volatile int got_usr1 = 0;
+static volatile int got_usr1_ret = 0;
static volatile int got_ttin = 0;
static volatile int got_ttou = 0;
(uint32_t)(sizeof("[init] SIGUSR1 handler OK\n") - 1));
}
+static void usr1_ret_handler(int sig) {
+ (void)sig;
+ got_usr1_ret = 1;
+}
+
static void ttin_handler(int sig) {
(void)sig;
got_ttin = 1;
(uint32_t)(sizeof("[init] sigaction/kill(SIGUSR1) OK\n") - 1));
}
+ // Verify that returning from a signal handler does not corrupt the user stack.
+ {
+ if (sys_sigaction(SIGUSR1, usr1_ret_handler, 0) < 0) {
+ sys_write(1, "[init] sigaction (sigreturn test) failed\n",
+ (uint32_t)(sizeof("[init] sigaction (sigreturn test) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ volatile uint32_t canary = 0x11223344U;
+ int me = sys_getpid();
+ if (sys_kill(me, SIGUSR1) < 0) {
+ sys_write(1, "[init] kill(SIGUSR1) (sigreturn test) failed\n",
+ (uint32_t)(sizeof("[init] kill(SIGUSR1) (sigreturn test) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (!got_usr1_ret) {
+ sys_write(1, "[init] SIGUSR1 not delivered (sigreturn test)\n",
+ (uint32_t)(sizeof("[init] SIGUSR1 not delivered (sigreturn test)\n") - 1));
+ sys_exit(1);
+ }
+
+ if (canary != 0x11223344U) {
+ sys_write(1, "[init] sigreturn test stack corruption\n",
+ (uint32_t)(sizeof("[init] sigreturn test stack corruption\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[init] sigreturn OK\n",
+ (uint32_t)(sizeof("[init] sigreturn OK\n") - 1));
+ }
+
fd = sys_open("/tmp/hello.txt", 0);
if (fd < 0) {
sys_write(1, "[init] tmpfs open2 failed\n",