kernel: complete redesign of signal handling

This commit is contained in:
K. Lange 2022-03-04 16:12:16 +09:00
parent 9f495ac854
commit bbebc7c128
8 changed files with 155 additions and 114 deletions

View File

@ -115,7 +115,6 @@ typedef struct process {
list_t * shm_mappings;
list_t * node_waits;
list_t * signal_queue;
char * signal_kstack;
node_t sched_node;
node_t sleep_node;
@ -269,6 +268,7 @@ extern void arch_save_floating(process_t * proc);
extern void arch_set_kernel_stack(uintptr_t);
extern void arch_enter_user(uintptr_t entrypoint, int argc, char * argv[], char * envp[], uintptr_t stack);
__attribute__((noreturn))
extern void arch_enter_signal_handler(uintptr_t,int);
extern void arch_enter_signal_handler(uintptr_t,int,struct regs*);
extern void arch_wakeup_others(void);
extern void arch_return_from_signal_handler(struct regs *r);

View File

@ -20,8 +20,6 @@ typedef struct {
extern void fix_signal_stacks(void);
extern int send_signal(pid_t process, int signal, int force_root);
extern int group_send_signal(pid_t group, int signal, int force_root);
extern void handle_signal(process_t * proc, signal_t * sig);
__attribute__((noreturn))
extern void return_from_signal_handler(void);
extern void handle_signal(process_t * proc, signal_t * sig, struct regs *r);
extern void process_check_signals(struct regs*);

View File

@ -12,6 +12,7 @@
#include <kernel/string.h>
#include <kernel/printf.h>
#include <kernel/spinlock.h>
#include <kernel/mmu.h>
#include <kernel/arch/aarch64/regs.h>
#include <kernel/arch/aarch64/gic.h>
@ -48,6 +49,40 @@ void arch_enter_user(uintptr_t entrypoint, int argc, char * argv[], char * envp[
__builtin_unreachable();
}
static void _kill_it(uintptr_t addr, const char * action, const char * desc, size_t size) {
dprintf("core %d (pid=%d %s): invalid stack for signal %s (%#zx '%s' %zu)\n",
this_core->cpu_id, this_core->current_process->id, this_core->current_process->name, action, addr, desc, size);
task_exit(((128 + SIGSEGV) << 8) | SIGSEGV);
}
#define PUSH(stack, type, item) do { \
stack -= sizeof(type); \
if (!mmu_validate_user_pointer((void*)(uintptr_t)stack,sizeof(type),MMU_PTR_WRITE)) \
_kill_it((uintptr_t)stack,"entry",#item,sizeof(type)); \
*((volatile type *) stack) = item; \
} while (0)
#define POP(stack, type, item) do { \
if (!mmu_validate_user_pointer((void*)(uintptr_t)stack,sizeof(type),0)) \
_kill_it((uintptr_t)stack,"return",#item,sizeof(type)); \
item = *((volatile type *) stack); \
stack += sizeof(type); \
} while (0)
void arch_return_from_signal_handler(struct regs *r) {
uintptr_t elr;
uintptr_t sp = r->user_sp;
POP(sp, uintptr_t, this_core->current_process->thread.context.saved[13]);
POP(sp, uintptr_t, this_core->current_process->thread.context.saved[12]);
for (int i = 0; i < 64; ++i) {
POP(sp, uint64_t, this_core->current_process->thread.fp_regs[63-i]);
}
POP(sp, struct regs, *r);
POP(sp, uint64_t, elr);
asm volatile ("msr ELR_EL1, %0" : "=r"(elr));
}
/**
* @brief Enter a userspace signal handler.
*
@ -61,14 +96,30 @@ void arch_enter_user(uintptr_t entrypoint, int argc, char * argv[], char * envp[
* @param entrypoint Userspace address of the signal handler, set by the process.
* @param signum Signal number that caused this entry.
*/
void arch_enter_signal_handler(uintptr_t entrypoint, int signum) {
void arch_enter_signal_handler(uintptr_t entrypoint, int signum, struct regs *r) {
uintptr_t elr;
uintptr_t sp = (r->user_sp - 128) & 0xFFFFFFFFFFFFFFF0;
asm volatile ("mrs %0, ELR_EL1" : "=r"(elr));
PUSH(sp, uint64_t, elr);
PUSH(sp, struct regs, *r);
for (int i = 0; i < 64; ++i) {
PUSH(sp, uint64_t, this_core->current_process->thread.fp_regs[i]);
}
PUSH(sp, uintptr_t, this_core->current_process->thread.context.saved[12]);
PUSH(sp, uintptr_t, this_core->current_process->thread.context.saved[13]);
asm volatile(
"msr ELR_EL1, %0\n" /* entrypoint */
"msr SP_EL0, %1\n" /* stack */
"msr SPSR_EL1, %2\n" /* spsr from context */
::
"r"(entrypoint),
"r"((this_core->current_process->syscall_registers->user_sp - 128) & 0xFFFFFFFFFFFFFFF0),
"r"(sp),
"r"(this_core->current_process->thread.context.saved[11]));
register uint64_t x0 __asm__("x0") = signum;

View File

@ -294,7 +294,7 @@ void aarch64_sync_enter(struct regs * r) {
ptrace_signal(SIGTRAP, PTRACE_EVENT_SINGLESTEP);
}
return;
goto _resume_user;
}
/* Magic thread exit */
@ -305,8 +305,8 @@ void aarch64_sync_enter(struct regs * r) {
/* Magic signal return */
if (elr == 0x8DEADBEEF && far == 0x8DEADBEEF) {
return_from_signal_handler();
return;
arch_return_from_signal_handler(r);
goto _resume_user;
}
/* System call */
@ -315,13 +315,13 @@ void aarch64_sync_enter(struct regs * r) {
// this_core->current_process->id, r->x0, elr);
extern void syscall_handler(struct regs *);
syscall_handler(r);
return;
goto _resume_user;
}
/* KVM is mad at us; usually means our code is broken or we neglected a cache. */
if (far == 0x1de7ec7edbadc0de) {
printf("kvm: blip (esr=%#zx, elr=%#zx; pid=%d [%s])\n", esr, elr, this_core->current_process->id, this_core->current_process->name);
return;
goto _resume_user;
}
/* Unexpected fault, eg. page fault. */
@ -333,6 +333,9 @@ void aarch64_sync_enter(struct regs * r) {
dprintf(" TPIDR_EL0=%#zx\n", tpidr_el0);
send_signal(this_core->current_process->id, SIGSEGV, 1);
_resume_user:
process_check_signals(r);
}
static void spin(void) {
@ -406,6 +409,8 @@ void aarch64_irq_enter(struct regs * r) {
}
aarch64_interrupt_dispatch(0);
process_check_signals(r);
}
/**

View File

@ -510,7 +510,7 @@ static void _page_fault(struct regs * r) {
/* 8DEADBEEFh is the magic ret-from-sig address. */
if (faulting_address == 0x8DEADBEEF) {
return_from_signal_handler();
arch_return_from_signal_handler(r);
return;
}
@ -613,13 +613,7 @@ static void _handle_irq(struct regs * r, int irq) {
#define EXC(i,n) case i: _exception(r, n); break;
#define IRQ(i) case i: _handle_irq(r,i-32); break;
struct regs * isr_handler(struct regs * r) {
this_core->interrupt_registers = r;
if (r->cs != 0x08 && this_core->current_process) {
this_core->current_process->time_switch = arch_perf_timer();
}
struct regs * isr_handler_inner(struct regs * r) {
switch (r->int_no) {
EXC(0,"divide-by-zero");
case 1: return _debug_int(r);
@ -690,3 +684,20 @@ struct regs * isr_handler(struct regs * r) {
return r;
}
struct regs * isr_handler(struct regs * r) {
int from_userspace = r->cs != 0x08;
this_core->interrupt_registers = r;
if (from_userspace && this_core->current_process) {
this_core->current_process->time_switch = arch_perf_timer();
}
struct regs * out = isr_handler_inner(r);
if (from_userspace && this_core->current_process) {
process_check_signals(out);
}
return out;
}

View File

@ -46,6 +46,48 @@ void arch_enter_user(uintptr_t entrypoint, int argc, char * argv[], char * envp[
"D"(argc), "S"(argv), "d"(envp));
}
static void _kill_it(void) {
dprintf("core %d (pid=%d %s): invalid stack for signal return\n",
this_core->cpu_id, this_core->current_process->id, this_core->current_process->name);
task_exit(((128 + SIGSEGV) << 8) | SIGSEGV);
}
#define PUSH(stack, type, item) do { \
stack -= sizeof(type); \
if (!mmu_validate_user_pointer((void*)(uintptr_t)stack,sizeof(type),MMU_PTR_WRITE)) \
_kill_it(); \
*((volatile type *) stack) = item; \
} while (0)
#define POP(stack, type, item) do { \
if (!mmu_validate_user_pointer((void*)(uintptr_t)stack,sizeof(type),0)) \
_kill_it(); \
item = *((volatile type *) stack); \
stack += sizeof(type); \
} while (0)
void arch_return_from_signal_handler(struct regs *r) {
for (int i = 0; i < 64; ++i) {
POP(r->rsp, uint64_t, this_core->current_process->thread.fp_regs[63-i]);
}
struct regs out;
POP(r->rsp, struct regs, out);
#define R(n) r-> n = out. n
R(r15); R(r14); R(r13); R(r12);
R(r11); R(r10); R(r9); R(r8);
R(rbp); R(rdi); R(rsi); R(rdx); R(rcx); R(rbx); R(rax);
R(rip);
R(rsp);
r->rflags = (out.rflags & 0xcd5) | (1 << 21) | (1 << 9) | ((r->rflags & (1 << 8)) ? (1 << 8) : 0);
}
/**
* @brief Enter a userspace signal handler.
*
@ -59,14 +101,21 @@ void arch_enter_user(uintptr_t entrypoint, int argc, char * argv[], char * envp[
* @param entrypoint Userspace address of the signal handler, set by the process.
* @param signum Signal number that caused this entry.
*/
void arch_enter_signal_handler(uintptr_t entrypoint, int signum) {
void arch_enter_signal_handler(uintptr_t entrypoint, int signum, struct regs *r) {
struct regs ret;
ret.cs = 0x18 | 0x03;
ret.ss = 0x20 | 0x03;
ret.rip = entrypoint;
ret.rflags = (1 << 21) | (1 << 9);
ret.rsp = (this_core->current_process->syscall_registers->rsp - 128 - 8) & 0xFFFFFFFFFFFFFFF0; /* ensure considerable alignment */
*(uintptr_t*)ret.rsp = 0x00000008DEADBEEF; /* arbitrarily chosen stack return sentinel IP */
ret.rsp = (r->rsp - 128) & 0xFFFFFFFFFFFFFFF0; /* ensure considerable alignment */
PUSH(ret.rsp, struct regs, *r);
for (int i = 0; i < 64; ++i) {
PUSH(ret.rsp, uint64_t, this_core->current_process->thread.fp_regs[i]);
}
PUSH(ret.rsp, uintptr_t, 0x00000008DEADBEEF);
asm volatile(
"pushq %0\n"

View File

@ -133,18 +133,6 @@ void switch_next(void) {
__builtin_unreachable();
}
if (this_core->current_process->flags & PROC_FLAG_STARTED) {
/* If this process has a signal pending, we save its current context - including
* the entire kernel stack - before resuming switch_task. */
if (!this_core->current_process->signal_kstack) {
if (this_core->current_process->signal_queue->length > 0) {
this_core->current_process->signal_kstack = malloc(KERNEL_STACK_SIZE);
memcpy(this_core->current_process->signal_kstack, (void*)(this_core->current_process->image.stack - KERNEL_STACK_SIZE), KERNEL_STACK_SIZE);
memcpy((thread_t*)&this_core->current_process->signal_state, (thread_t*)&this_core->current_process->thread, sizeof(thread_t));
}
}
}
/* Mark the process as running and started. */
__sync_or_and_fetch(&this_core->current_process->flags, PROC_FLAG_STARTED);
@ -196,17 +184,6 @@ void switch_task(uint8_t reschedule) {
* from a task switch and have been awoken if we were sleeping. */
if (arch_save_context(&this_core->current_process->thread) == 1) {
arch_restore_floating((process_t*)this_core->current_process);
fix_signal_stacks();
if (!(this_core->current_process->flags & PROC_FLAG_FINISHED)) {
if (this_core->current_process->signal_queue->length > 0) {
node_t * node = list_dequeue(this_core->current_process->signal_queue);
signal_t * sig = node->value;
free(node);
handle_signal((process_t*)this_core->current_process,sig);
}
}
return;
}
@ -430,7 +407,6 @@ process_t * spawn_init(void) {
init->wait_queue = list_create("process wait queue (init)", init);
init->shm_mappings = list_create("process shm mapping (init)", init);
init->signal_queue = list_create("process signal queue (init)", init);
init->signal_kstack = NULL; /* Initialized later */
init->sched_node.prev = NULL;
init->sched_node.next = NULL;
@ -538,10 +514,6 @@ process_t * spawn_process(volatile process_t * parent, int flags) {
extern void tree_remove_reparent_root(tree_t * tree, tree_node_t * node);
void process_reap(process_t * proc) {
if (proc->signal_kstack) {
free(proc->signal_kstack);
}
if (proc->tracees) {
while (proc->tracees->length) {
free(list_pop(proc->tracees));

View File

@ -72,7 +72,7 @@ char isdeadly[] = {
[SIGCAT ] = 0,
};
void handle_signal(process_t * proc, signal_t * sig) {
void handle_signal(process_t * proc, signal_t * sig, struct regs *r) {
uintptr_t handler = sig->handler;
uintptr_t signum = sig->signum;
free(sig);
@ -120,58 +120,7 @@ void handle_signal(process_t * proc, signal_t * sig) {
return;
}
arch_enter_signal_handler(handler, signum);
}
list_t * rets_from_sig;
void return_from_signal_handler(void) {
if (__builtin_expect(!rets_from_sig, 0)) {
rets_from_sig = list_create("global return-from-signal queue",NULL);
}
spin_lock(sig_lock);
list_insert(rets_from_sig, (process_t *)this_core->current_process);
spin_unlock(sig_lock);
switch_next();
}
void fix_signal_stacks(void) {
uint8_t redo_me = 0;
if (rets_from_sig) {
spin_lock(sig_lock_b);
while (rets_from_sig->head) {
spin_lock(sig_lock);
node_t * n = list_dequeue(rets_from_sig);
spin_unlock(sig_lock);
if (!n) {
continue;
}
process_t * p = n->value;
free(n);
if (p == this_core->current_process) {
redo_me = 1;
continue;
}
memcpy(&p->thread, &p->signal_state, sizeof(thread_t));
if (!p->signal_kstack) {
printf("Cannot restore signal stack for pid=%d - unset?\n", p->id);
} else {
memcpy((char *)(p->image.stack - KERNEL_STACK_SIZE + 0x1000), p->signal_kstack + 0x1000, KERNEL_STACK_SIZE - 0x1000);
free(p->signal_kstack);
p->signal_kstack = NULL;
}
make_process_ready(p);
}
spin_unlock(sig_lock_b);
}
if (redo_me) {
spin_lock(sig_lock);
list_insert(rets_from_sig, (process_t *)this_core->current_process);
spin_unlock(sig_lock);
switch_next();
}
arch_enter_signal_handler(handler, signum, r);
}
int send_signal(pid_t process, int signal, int force_root) {
@ -230,15 +179,6 @@ int send_signal(pid_t process, int signal, int force_root) {
make_process_ready(receiver);
}
if (receiver == this_core->current_process) {
/* Forces us to be rescheduled and enter signal handler */
if (receiver->signal_kstack) {
switch_next();
} else {
switch_task(0);
}
}
return 0;
}
@ -270,3 +210,18 @@ int group_send_signal(pid_t group, int signal, int force_root) {
return !!killed_something;
}
void process_check_signals(struct regs * r) {
if (this_core->current_process &&
!(this_core->current_process->flags & PROC_FLAG_FINISHED) &&
this_core->current_process->signal_queue &&
this_core->current_process->signal_queue->length > 0) {
node_t * node = list_dequeue(this_core->current_process->signal_queue);
signal_t * sig = node->value;
free(node);
handle_signal((process_t*)this_core->current_process,sig,r);
}
}