diff -Naur -X exclude-files ac_cur/arch/um/include/kern_util.h ac/arch/um/include/kern_util.h --- ac_cur/arch/um/include/kern_util.h Fri Oct 5 14:43:33 2001 +++ ac/arch/um/include/kern_util.h Fri Oct 5 15:01:48 2001 @@ -70,11 +70,7 @@ extern void *process_state(void *t, unsigned long *cr2_out, int *err_out); extern struct sys_pt_regs *syscall_state(void *t, void **stack_out, int *size_out); -extern int have_signals(void *t, int altstack); extern void syscall_trace(void); -extern void save_altstack(void *t, unsigned long sp); -extern struct sys_pt_regs *altstack_state(void *t, void **stack_out, - int *size_out); extern int hz(void); extern int switching_modes(void *t); extern void idle_timer(void); @@ -85,7 +81,6 @@ extern void tracing_reboot(void); extern void tracing_halt(void); extern void tracing_cb(void (*proc)(void *), void *arg); -extern void probe_stack(unsigned long sp); extern void debugger_signal(int status, int pid); extern void child_signal(int pid, int status); extern int init_ptrace_proxy(int idle_pid, int startup, int stop); @@ -98,7 +93,6 @@ extern void finish_fork_handler(int sig); extern int user_context(unsigned long sp); extern void timer_irq(int user_mode); -extern void signal_deliverer(void); extern void set_repeat_syscall(int again); extern int get_repeat_syscall(void *t); extern void set_sigreturn_syscall(int syscall); @@ -111,7 +105,6 @@ extern void *round_up(unsigned long addr); extern void *round_down(unsigned long addr); extern void bad_segv(unsigned long address, unsigned long ip, int is_write); -extern void handling_signal(void *t); extern int config_gdb(char *str); extern int remove_gdb(void); extern char *uml_strdup(char *string); @@ -119,6 +112,10 @@ extern void protect_kernel_mem(void); extern unsigned long get_kmem_end(void); extern void set_kmem_end(unsigned long); +extern void *diff_stacks(int size, char *stack1, struct sys_pt_regs *regs1, + char *stack2, struct sys_pt_regs *regs2, + char **mask_out, struct sys_pt_regs *regs_out, + struct sys_pt_regs *regs_out_mask); #endif /* diff -Naur -X exclude-files ac_cur/arch/um/include/signal_kern.h ac/arch/um/include/signal_kern.h --- ac_cur/arch/um/include/signal_kern.h Wed Dec 31 19:00:00 1969 +++ ac/arch/um/include/signal_kern.h Fri Oct 5 15:01:48 2001 @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SIGNAL_KERN_H__ +#define __SIGNAL_KERN_H__ + +#include "sysdep/ptrace.h" + +extern void signal_deliverer(int sig); +extern struct sys_pt_regs *signal_state(void *t); +extern int probe_stack(unsigned long sp, int delta); +extern int have_signals(void *t); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Naur -X exclude-files ac_cur/arch/um/include/signal_user.h ac/arch/um/include/signal_user.h --- ac_cur/arch/um/include/signal_user.h Wed Dec 31 19:00:00 1969 +++ ac/arch/um/include/signal_user.h Fri Oct 5 15:01:48 2001 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SIGNAL_USER_H__ +#define __SIGNAL_USER_H__ + +extern int signal_stack_size; + +extern void change_sig(int signal, int on); +extern void signal_handler(void *task, unsigned long handler, int sig); +extern void set_sigstack(void *stack, int size); +extern void set_handler(int sig, void (*handler)(int), int flags, ...); +extern void setup_stack(unsigned long stack_top, struct sys_pt_regs *regs_out); +extern void setup_signal_stack(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff -Naur -X exclude-files ac_cur/arch/um/include/user_util.h ac/arch/um/include/user_util.h --- ac_cur/arch/um/include/user_util.h Fri Oct 5 14:43:33 2001 +++ ac/arch/um/include/user_util.h Fri Oct 5 15:01:48 2001 @@ -66,7 +66,6 @@ extern void usr1_pid(int pid); extern void cont_pid(int pid); extern int __personality(int); -extern void signal_handler(void *task, unsigned long handler, int sig); extern int syscall_handler(void *unused); extern int do_syscall(void *task, int pid); extern int wait_for_stop(int pid, int sig, int cont_type); @@ -82,8 +81,6 @@ extern void set_timers(int set_signal); extern int clone_and_wait(int (*fn)(void *), void *arg, void *sp, int flags); extern int input_loop(void); -extern void set_handler(int sig, void (*handler)(int), int flags, ...); -extern void set_sigstack(void *stack); extern void continue_execing_proc(int pid); extern int linux_main(int argc, char **argv); extern void remap_data(void *segment_start, void *segment_end); @@ -91,13 +88,9 @@ extern void continue_fork(void *task, int pid, struct sys_pt_regs *regs); extern void input_cb(void (*proc)(void *), void *arg, int arg_len); extern void setup_input(void); -extern int exit_kernel(int pid, void *task, int *signal_out); -extern int alt_stack_handler(void *arg); -extern int setup_altstack(unsigned long stack, int stack_size); -extern void process_stack_handler(int sig); +extern int exit_kernel(int pid, void *task); extern int get_pty(void); extern void save_signal_state(int *sig_ptr); -extern void change_sig(int signal, int on); extern void fill_in_sigcontext(void *sc, struct sys_pt_regs *regs, unsigned long cr2, int err); extern int activate_fd(int irq, int fd, void *dev_id); @@ -110,7 +103,8 @@ extern void cooked(int fd); extern int switcheroo(int fd, int prot, void *from, void *to, int size); extern void idle_sleep(int secs); -extern int get_one_stack(char **stack_out, struct sys_pt_regs *regs_out); +extern int get_one_stack(int (*proc)(void *), void *arg, char **stack_out, + struct sys_pt_regs *regs_out); extern void setup_machinename(char *machine_out); extern void setup_hostinfo(void); extern void add_arg(char *cmd_line, char *arg); diff -Naur -X exclude-files ac_cur/arch/um/kernel/irq_user.c ac/arch/um/kernel/irq_user.c --- ac_cur/arch/um/kernel/irq_user.c Fri Oct 5 14:15:35 2001 +++ ac/arch/um/kernel/irq_user.c Fri Oct 5 15:01:48 2001 @@ -14,6 +14,7 @@ #include "kern_util.h" #include "user.h" #include "process.h" +#include "signal_user.h" struct irq_fd { struct irq_fd *next; @@ -147,10 +148,9 @@ flags = on_sigstack ? SA_ONSTACK : 0; set_handler(SIGVTALRM, (__sighandler_t) alarm_handler, - flags | SA_NODEFER | SA_RESTART, SIGUSR1, SIGIO, - SIGUSR2, -1); + flags | SA_NODEFER | SA_RESTART, SIGUSR1, SIGIO, -1); set_handler(SIGIO, (__sighandler_t) irq_handler, flags | SA_RESTART, - SIGUSR1, SIGIO, SIGUSR2, -1); + SIGUSR1, SIGIO, -1); } /* diff -Naur -X exclude-files ac_cur/arch/um/kernel/process.c ac/arch/um/kernel/process.c --- ac_cur/arch/um/kernel/process.c Fri Oct 5 14:43:33 2001 +++ ac/arch/um/kernel/process.c Fri Oct 5 15:01:48 2001 @@ -24,6 +24,8 @@ #include "kern_util.h" #include "user.h" #include "process.h" +#include "signal_kern.h" +#include "signal_user.h" #include "sysdep/ptrace.h" #include "sysdep/sigcontext.h" @@ -47,41 +49,12 @@ kill(pid, SIGCONT); } -void set_sigstack(void *sig_stack) -{ - stack_t stack; - - stack.ss_sp = (__ptr_t) sig_stack; - stack.ss_flags = 0; - stack.ss_size = 2 * page_size() - sizeof(void *); - if(sigaltstack(&stack, NULL) != 0) - panic("sigaltstack failed"); -} - -void set_handler(int sig, void (*handler)(int), int flags, ...) -{ - struct sigaction action; - va_list ap; - int mask; - - va_start(ap, flags); - action.sa_handler = handler; - sigemptyset(&action.sa_mask); - while((mask = va_arg(ap, int)) != -1){ - sigaddset(&action.sa_mask, mask); - } - action.sa_flags = flags; - action.sa_restorer = NULL; - if(sigaction(sig, &action, NULL) < 0) - panic("sigaction failed"); -} - void init_new_thread(void *sig_stack, void (*usr1_handler)(int)) { int flags = 0; if(sig_stack != NULL){ - set_sigstack(sig_stack); + set_sigstack(sig_stack, 2 * page_size()); flags = SA_ONSTACK; } set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags, @@ -94,11 +67,10 @@ SIGUSR1, SIGIO, -1); set_handler(SIGBUS, (__sighandler_t) sig_handler, flags, SIGUSR1, SIGIO, -1); - set_handler(SIGUSR2, process_stack_handler, SA_NODEFER, SIGUSR1, -1); - change_sig(SIGUSR2, 1); if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1); signal(SIGCHLD, SIG_IGN); - set_timers(1); + signal(SIGHUP, SIG_IGN); + set_timers(1); /* XXX A bit of a race here */ init_irq_signals(sig_stack != NULL); } @@ -185,20 +157,6 @@ panic("ptrace failed in trace_myself"); } -void signal_handler(void *task, unsigned long h, int sig) -{ - void (*handler)(int, struct sigcontext); - void *regs; - unsigned long cr2; - int err; - UM_ALLOCATE_SC(sc); - - regs = process_state(task, &cr2, &err); - fill_in_sigcontext(&sc, regs, cr2, err); - handler = (void (*)(int, struct sigcontext)) h; - (*handler)(sig, sc); -} - void continue_fork(void *task, int pid, struct sys_pt_regs *regs) { if(ptrace(PTRACE_CONT, pid, 0, SIGUSR1) < 0) @@ -208,38 +166,8 @@ tracer_panic("Couldn't restore registers"); } -int setup_altstack(unsigned long stack, int stack_size) -{ - struct sys_pt_regs *regs; - unsigned long sp; - int pid; - - sp = stack + stack_size - sizeof(void *); - pid = clone(alt_stack_handler, (void *) sp, - CLONE_VM | CLONE_FILES | SIGCHLD, NULL); - if(pid < 0) return(-errno); - wait_for_stop(pid, SIGSTOP, PTRACE_CONT); - regs = altstack_state(NULL, NULL, NULL); - if(ptrace_getregs(pid, regs) < 0) - panic("Couldn't get altstack state"); - save_altstack(NULL, UM_SP(regs)); - kill(pid, SIGKILL); - if(waitpid(pid, NULL, WNOHANG) < 0) - printk("setup_altstack : waitpid failed, errno = %d\n", - errno); - return(0); -} - -void change_sig(int signal, int on) -{ - sigset_t sigset; - - sigemptyset(&sigset); - sigaddset(&sigset, signal); - sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, NULL); -} - -int get_one_stack(char **stack_out, struct sys_pt_regs *regs_out) +int get_one_stack(int (*proc)(void *), void *arg, char **stack_out, + struct sys_pt_regs *regs_out) { void *stack; unsigned long sp; @@ -252,7 +180,7 @@ exit(1); } sp = stack_sp((unsigned long) stack); - pid = clone(syscall_handler, (void *) sp, SIGCHLD, NULL); + pid = clone(proc, (void *) sp, SIGCHLD | CLONE_VM, arg); if(pid < 0){ perror("get_one_stack : couldn't start thread"); exit(1); @@ -265,13 +193,6 @@ if(ptrace_getregs(pid, regs_out) < 0){ perror("get_one_stack : couldn't get registers"); exit(1); - } - sp = UM_SP(regs_out); - sp &= ~sizeof(unsigned long); - while(sp < (unsigned long) stack + page_size()){ - *((unsigned long *) sp) = ptrace(PTRACE_PEEKDATA, pid, - (void *) sp, NULL); - sp += sizeof(unsigned long); } kill(pid, SIGKILL); kill(pid, SIGCONT); diff -Naur -X exclude-files ac_cur/arch/um/kernel/process_kern.c ac/arch/um/kernel/process_kern.c --- ac_cur/arch/um/kernel/process_kern.c Fri Oct 5 14:43:33 2001 +++ ac/arch/um/kernel/process_kern.c Fri Oct 5 15:06:00 2001 @@ -27,6 +27,8 @@ #include "user_util.h" #include "kern_util.h" #include "kern.h" +#include "signal_kern.h" +#include "signal_user.h" struct cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { -1, NULL } }; @@ -236,6 +238,7 @@ } char *generic_stack = NULL; +char *generic_stack_mask = NULL; int generic_stack_size = 0; struct sys_pt_regs generic_regs; struct sys_pt_regs generic_regs_mask; @@ -251,7 +254,7 @@ task->thread.syscall_regs.regs[i] = generic_regs.regs[i]; if(generic_regs_mask.regs[i]) task->thread.syscall_regs.regs[i] += - task->thread.kernel_stack + PAGE_SIZE; + task->thread.kernel_stack + 2 * PAGE_SIZE; } } @@ -554,72 +557,10 @@ current->thread.repeat_syscall = again; } -void save_altstack(void *t, unsigned long sp) -{ - struct task_struct *task; - void *stack; - int n; - - if(t == NULL) task = current; - else task = t; - if(task->thread.altstack != NULL) kfree(task->thread.altstack); - n = PAGE_SIZE - (sp & ~PAGE_MASK); - stack = kmalloc(n, GFP_KERNEL); - if(stack == NULL) panic("save_altstack : kmalloc failed"); - memcpy(stack, (void *) sp, n); - task->thread.altstack = stack; - task->thread.altstack_size = n; -} - -struct sys_pt_regs *altstack_state(void *t, void **stack_out, int *size_out) -{ - struct task_struct *task; - - if(t == NULL) task = current; - else task = t; - if(stack_out != NULL) *stack_out = task->thread.altstack; - if(size_out != NULL) *size_out = task->thread.altstack_size; - return(&task->thread.altstack_regs); -} - -extern int signal_frame_size; - -void probe_stack(unsigned long sp) -{ - int n, delta; - - delta = signal_frame_size; - get_user(n, (int *) sp); - put_user(n, (int *) sp); - get_user(n, (int *) (sp - delta)); - put_user(n, (int *) (sp - delta)); -} - void dump_thread(struct pt_regs *regs, struct user *u) { } -void handling_signal(void *t) -{ - struct task_struct *task = t; - - task->thread.signal.state = SIGNAL_HANDLING; -} - -int have_signals(void *t, int altstack) -{ - struct task_struct *task; - - if(t == NULL) task = current; - else task = t; - if(task->thread.signal.state == SIGNAL_PENDING){ - if((altstack && (task->thread.signal.sp != 0)) || - (!altstack && (task->thread.signal.sp == 0))) - return(1); - } - return(0); -} - int switching_modes(void *t) { struct task_struct *task; @@ -638,12 +579,12 @@ panic("disable_hlt"); } +extern int signal_frame_size; + void interrupt_end(void) { if(current->need_resched) schedule(); do_signal(NULL, NULL); - if(current->thread.signal.state != SIGNAL_NONE) - probe_stack(UM_SP(¤t->thread.process_regs)); } void *um_kmalloc(int size) @@ -676,26 +617,24 @@ EXPORT_SYMBOL(not_implemented); -void setup_kernel_stack(void) -{ - struct sys_pt_regs regs1, regs2; - char *stack1, *stack2; - unsigned long s1, s2, n1, n2, off1, off2; - int size, i; +void *diff_stacks(int size, char *stack1, struct sys_pt_regs *regs1, + char *stack2, struct sys_pt_regs *regs2, char **mask_out, + struct sys_pt_regs *regs_out, + struct sys_pt_regs *regs_mask_out) +{ + char *mask; + void *new; + unsigned long s1, s2, n1, n2, off1, off2, i; - size = get_one_stack(&stack1, ®s1); - if(get_one_stack(&stack2, ®s2) != size){ - printf("setup_kernel_stack : differing stack sizes\n"); - exit(1); - } s1 = (unsigned long) stack1; s2 = (unsigned long) stack2; - generic_stack = malloc(size); - generic_stack_size = size; - if(generic_stack == NULL){ - perror("setup_kernel_stack : allocating new stack"); + new = malloc(size); + mask = malloc(size); + if((new == NULL) || (mask == NULL)){ + perror("diff_stacks : allocating new stack and mask"); exit(1); } + memset(mask, 0, size); for(i = PAGE_SIZE - size; i < PAGE_SIZE - sizeof(void *); i++){ /* This is horribly word-length- and byte-order-dependent */ n1 = stack1[i] | (stack1[i + 1] << 8) | @@ -716,29 +655,51 @@ off1 = n1 & ~PAGE_MASK; off2 = n2 & ~PAGE_MASK; if((off1 == off2) && (off1 > PAGE_SIZE - size)){ - printf("Found a frame pointer on the stack\n"); - exit(1); + *((unsigned long *) &stack1[i]) -= s1 + PAGE_SIZE; + mask[i - (PAGE_SIZE - size)] = 1; + i += sizeof(unsigned long); + } + } + memcpy((char *) new, &stack1[PAGE_SIZE - size], size); + for(i = 0; i < sizeof(regs1->regs)/sizeof(regs1->regs[0]); i++){ + regs_out->regs[i] = regs1->regs[i]; + regs_mask_out->regs[i] = 0; + if((regs1->regs[i] == regs2->regs[i]) || + ((regs1->regs[i] & PAGE_MASK) != s1) || + ((regs2->regs[i] & PAGE_MASK) != s2)) continue; + off1 = regs1->regs[i] & ~PAGE_MASK; + off2 = regs2->regs[i] & ~PAGE_MASK; + if((off1 == off2) && (off1 >= PAGE_SIZE - size)){ + regs_out->regs[i] = regs1->regs[i] - s1 - PAGE_SIZE; + regs_mask_out->regs[i] = 1; } } - memcpy(generic_stack, &stack1[PAGE_SIZE - size], size); + *mask_out = mask; + return(new); +} + +void setup_kernel_stack(void) +{ + struct sys_pt_regs regs1, regs2; + char *stack1, *stack2; + int size; + + size = get_one_stack(syscall_handler, NULL, &stack1, ®s1); + if(get_one_stack(syscall_handler, NULL, &stack2, ®s2) != size){ + printf("setup_kernel_stack : differing stack sizes\n"); + exit(1); + } + + generic_stack = diff_stacks(size, stack1, ®s1, stack2, ®s2, + &generic_stack_mask, &generic_regs, + &generic_regs_mask); + generic_stack_size = size; + if((munmap(stack1, PAGE_SIZE) < 0) || (munmap(stack2, PAGE_SIZE) < 0)){ perror("setup_kernel_stack : unmapping temp stacks"); exit(1); } - for(i = 0; i < sizeof(regs1.regs)/sizeof(regs1.regs[0]); i++){ - generic_regs.regs[i] = regs1.regs[i]; - generic_regs_mask.regs[i] = 0; - if((regs1.regs[i] == regs2.regs[i]) || - ((regs1.regs[i] & PAGE_MASK) != s1) || - ((regs2.regs[i] & PAGE_MASK) != s2)) continue; - off1 = regs1.regs[i] & ~PAGE_MASK; - off2 = regs2.regs[i] & ~PAGE_MASK; - if((off1 == off2) && (off1 >= PAGE_SIZE - size)){ - generic_regs.regs[i] = regs1.regs[i] - s1; - generic_regs_mask.regs[i] = 1; - } - } } int user_context(unsigned long sp) @@ -791,8 +752,9 @@ { unsigned long start_stack, end_stack; - start_stack = (unsigned long) current; - end_stack = start_stack + PAGE_SIZE * 4; + if(current_task == &init_task) return; + start_stack = (unsigned long) current + PAGE_SIZE; + end_stack = (unsigned long) current + PAGE_SIZE * 4; protect(physmem, start_stack - physmem, 1, 1, 1); protect(end_stack, high_physmem - end_stack, 1, 1, 1); } @@ -801,10 +763,11 @@ { unsigned long start_stack, end_stack; - start_stack = (unsigned long) current; - end_stack = start_stack + PAGE_SIZE * 4; - protect(physmem, start_stack - physmem, 1, 1, 1); - protect(end_stack, high_physmem - end_stack, 1, 1, 1); + if(current_task == &init_task) return; + start_stack = (unsigned long) current + PAGE_SIZE; + end_stack = (unsigned long) current + PAGE_SIZE * 4; + protect(physmem, start_stack - physmem, 1, 0, 1); + protect(end_stack, high_physmem - end_stack, 1, 0, 1); } /* diff -Naur -X exclude-files ac_cur/arch/um/kernel/signal_kern.c ac/arch/um/kernel/signal_kern.c --- ac_cur/arch/um/kernel/signal_kern.c Fri Oct 5 14:16:42 2001 +++ ac/arch/um/kernel/signal_kern.c Fri Oct 5 15:15:57 2001 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -11,15 +11,49 @@ #include "linux/kernel.h" #include "linux/smp_lock.h" #include "linux/module.h" +#include "linux/slab.h" #include "asm/signal.h" #include "asm/uaccess.h" #include "user_util.h" #include "kern_util.h" +#include "signal_kern.h" +#include "signal_user.h" #include "kern.h" EXPORT_SYMBOL(block_signals); EXPORT_SYMBOL(unblock_signals); +struct sys_pt_regs *signal_state(void *t) +{ + struct task_struct *task = t; + + return(&task->thread.altstack_regs); +} + +int probe_stack(unsigned long sp, int delta) +{ + int n; + + if((get_user(n, (int *) sp) != 0) || + (put_user(n, (int *) sp) != 0) || + (get_user(n, (int *) (sp - delta)) != 0) || + (put_user(n, (int *) (sp - delta)) != 0)) + return(-EFAULT); + return(0); +} + +int have_signals(void *t) +{ + struct task_struct *task; + int ret; + + if(t == NULL) task = current; + else task = t; + ret = (task->thread.signal.state == SIGNAL_PENDING); + task->thread.signal.state = SIGNAL_NONE; + return(ret); +} + int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) { if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) @@ -62,14 +96,17 @@ /* * OK, we're invoking a handler */ -static int handle_signal(struct task_struct *task, unsigned long signr, - struct k_sigaction *ka, sigset_t *oldset, - unsigned long *error) +static int handle_signal(unsigned long signr, struct k_sigaction *ka, + sigset_t *oldset, unsigned long *error, + int *again_out) { + struct signal_context *context; __sighandler_t handler; + unsigned long sp; sigset_t save; - int ret = 0; + int frame_size; + if(again_out) *again_out = 0; if((error != NULL) && (*error != 0)){ switch (*error) { case -ERESTARTNOHAND: @@ -83,50 +120,67 @@ } /* fallthrough */ case -ERESTARTNOINTR: - ret = 1; + if(again_out) *again_out = 1; break; } } handler = ka->sa.sa_handler; - save = *oldset; if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; if (!(ka->sa.sa_flags & SA_NODEFER)) { - spin_lock_irq(&task->sigmask_lock); - sigorsets(&task->blocked,¤t->blocked,&ka->sa.sa_mask); - sigaddset(&task->blocked,signr); - recalc_sigpending(task); - spin_unlock_irq(&task->sigmask_lock); + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,signr); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); } - if(task->thread.signal.state != SIGNAL_NONE) - panic("Too many queued signals"); - task->thread.signal.signal = signr; - task->thread.signal.sp = 0; - if (ka->sa.sa_flags & SA_ONSTACK) { - if (! on_sig_stack(UM_SP(¤t->thread.process_regs))) - task->thread.signal.sp = - current->sas_ss_sp + current->sas_ss_size; + sp = UM_SP(¤t->thread.process_regs); + + if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0)) + sp = current->sas_ss_sp + current->sas_ss_size; + + frame_size = 4 * sizeof(void *) + sizeof(*context) + + 4 * sizeof(void *) + signal_stack_size; + + if(probe_stack(sp - sizeof(void *), frame_size) < 0){ + if(signr == SIGSEGV){ + struct k_sigaction *ka; + + ka = ¤t->sig->action[SIGSEGV - 1]; + ka->sa.sa_handler = SIG_DFL; + } + force_sig(SIGSEGV, current); + return(1); } - task->thread.signal.handler = (unsigned long) handler; - task->thread.signal.state = SIGNAL_PENDING; - task->thread.saved_sigs = save; + current->thread.signal.signal = signr; + current->thread.signal.handler = (unsigned long) handler; + current->thread.signal.state = SIGNAL_PENDING; + + sp -= 4 * sizeof(void *) + sizeof(*context); + context = (struct signal_context *) sp; + if(error != NULL) + UM_SET_SYSCALL_RETURN(¤t->thread.process_regs, *error); + context->regs = current->thread.process_regs; + context->repeat = current->thread.repeat_syscall; + context->sigs = save; + context->prev = current->thread.signal_context; + current->thread.signal_context = context; - return(ret); + sp -= 4 * sizeof(void *); + setup_stack(sp, ¤t->thread.altstack_regs); + + return(0); } /* * Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. - * - * Note that we go through the signals twice: once to check the signals that - * the kernel can handle, and then we build all the user-level signal handling - * stack-frames in one go after that. */ static int kern_do_signal(sigset_t *oldset, unsigned long *error, @@ -134,7 +188,7 @@ { siginfo_t info; struct k_sigaction *ka; - int again; + int err; if (!oldset) oldset = ¤t->blocked; @@ -235,18 +289,15 @@ } /* Whee! Actually deliver the signal. */ - again = handle_signal(current, signr, ka, oldset, error); - if(again_out && (*again_out == 0)) *again_out = again; - return(1); + err = handle_signal(signr, ka, oldset, error, again_out); + if(!err) return(1); } return(0); } int do_signal(unsigned long *error, int *again_out) { - if(current->thread.signal.state == SIGNAL_NONE) - return(kern_do_signal(NULL, error, again_out)); - return(0); + return(kern_do_signal(NULL, error, again_out)); } /* @@ -299,29 +350,15 @@ } } -void signal_deliverer(void) +void signal_deliverer(int sig) { - struct task_struct *task; - struct sys_pt_regs regs; - sigset_t sigs; unsigned long handler; - int repeat, signal; + int signal; -#ifdef CONFIG_SMP -#error signal_deliverer needs some SMP work -#else - task = current; -#endif - regs = task->thread.process_regs; - repeat = task->thread.repeat_syscall; - sigs = task->thread.saved_sigs; - signal = task->thread.signal.signal; - handler = task->thread.signal.handler; - task->thread.signal.state = SIGNAL_NONE; - signal_handler(task, handler, signal); - task->thread.process_regs = regs; - task->thread.repeat_syscall = repeat; - task->thread.saved_sigs = sigs; + stop_pid(getpid()); + signal = current->thread.signal.signal; + handler = current->thread.signal.handler; + signal_handler(current, handler, signal); } void set_sigreturn_syscall(int syscall) @@ -331,14 +368,19 @@ int sys_sigreturn(struct sys_pt_regs regs) { - sigdelsetmask(¤t->thread.saved_sigs, ~_BLOCKABLE); + struct signal_context *context = current->thread.signal_context; + + sigdelsetmask(&context->sigs, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); - current->blocked = current->thread.saved_sigs; + current->blocked = context->sigs; recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); + current->thread.process_regs = context->regs; + current->thread.repeat_syscall = context->repeat; + current->thread.signal_context = context->prev; UM_SYSCALL_NR(¤t->thread.process_regs) = current->thread.sigreturn_syscall; - return(UM_SYSCALL_RET(®s)); + return(UM_SYSCALL_RET(¤t->thread.process_regs)); } /* diff -Naur -X exclude-files ac_cur/arch/um/kernel/signal_user.c ac/arch/um/kernel/signal_user.c --- ac_cur/arch/um/kernel/signal_user.c Fri Oct 5 14:15:35 2001 +++ ac/arch/um/kernel/signal_user.c Fri Oct 5 15:01:48 2001 @@ -3,13 +3,150 @@ * Licensed under the GPL */ +#include +#include #include #include #include +#include +#include +#include #include "user_util.h" +#include "kern_util.h" #include "user.h" +#include "signal_user.h" +#include "signal_kern.h" +#include "sysdep/sigcontext.h" extern int timer_on; + +char *signal_stack = NULL; +char *signal_stack_mask = NULL; +int signal_stack_size = 0; +struct sys_pt_regs signal_regs; +struct sys_pt_regs signal_regs_mask; + +static int setup_signal_tramp(void *stack) +{ + set_sigstack(stack, page_size()); + set_handler(SIGUSR1, signal_deliverer, SA_ONSTACK, -1); + usr1_pid(getpid()); +} + +void setup_signal_stack(void) +{ + struct sys_pt_regs regs1, regs2; + char *sigstack1, *sigstack2, *stack1, *stack2; + int size; + + sigstack1 = mmap(NULL, page_size(), PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + sigstack2 = mmap(NULL, page_size(), PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if((sigstack1 == MAP_FAILED) || (sigstack2 == MAP_FAILED)){ + perror("setup_signal_stack : couldn't mmap stacks"); + exit(1); + } + size = get_one_stack(setup_signal_tramp, sigstack1, &stack1, ®s1); + if(get_one_stack(setup_signal_tramp, sigstack2, &stack2, + ®s2) != size){ + printf("setup_signal_stack : differing stack sizes\n"); + exit(1); + } + + signal_stack = diff_stacks(size, sigstack1, ®s1, sigstack2, ®s2, + &signal_stack_mask, &signal_regs, + &signal_regs_mask); + signal_stack_size = size; + + if((munmap(sigstack1, page_size()) < 0) || + (munmap(sigstack2, page_size()) < 0) || + (munmap(stack1, page_size()) < 0) || + (munmap(stack2, page_size()) < 0)){ + perror("setup_signal_stack : unmapping temp stacks"); + exit(1); + } +} + +void setup_stack(unsigned long stack_top, struct sys_pt_regs *regs_out) +{ + char *frame; + unsigned long val; + int i, n; + + n = sizeof(signal_regs.regs)/sizeof(signal_regs.regs[0]); + for(i = 0; i < n; i++){ + regs_out->regs[i] = signal_regs.regs[i]; + if(signal_regs_mask.regs[i]) + regs_out->regs[i] += stack_top; + } + + frame = (char *) (stack_top - signal_stack_size); + for(i = 0; i < signal_stack_size;){ + if(signal_stack_mask[i] == 0){ + frame[i] = signal_stack[i]; + i++; + } + else { + val = *((unsigned long *) &signal_stack[i]); + val += stack_top; + *((unsigned long *) &frame[i]) = val; + i += sizeof(unsigned long); + } + } +} + +void set_sigstack(void *sig_stack, int size) +{ + stack_t stack; + + stack.ss_sp = (__ptr_t) sig_stack; + stack.ss_flags = 0; + stack.ss_size = size - sizeof(void *); + if(sigaltstack(&stack, NULL) != 0) + panic("sigaltstack failed"); +} + +void set_handler(int sig, void (*handler)(int), int flags, ...) +{ + struct sigaction action; + va_list ap; + int mask; + + va_start(ap, flags); + action.sa_handler = handler; + sigemptyset(&action.sa_mask); + while((mask = va_arg(ap, int)) != -1){ + sigaddset(&action.sa_mask, mask); + } + action.sa_flags = flags; + action.sa_restorer = NULL; + if(sigaction(sig, &action, NULL) < 0) + panic("sigaction failed"); +} + +void change_sig(int signal, int on) +{ + sigset_t sigset; + + sigemptyset(&sigset); + sigaddset(&sigset, signal); + sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, NULL); +} + +void signal_handler(void *task, unsigned long h, int sig) +{ + void (*handler)(int, struct sigcontext); + void *regs; + unsigned long cr2; + int err; + UM_ALLOCATE_SC(sc); + + regs = process_state(task, &cr2, &err); + fill_in_sigcontext(&sc, regs, cr2, err); + handler = (void (*)(int, struct sigcontext)) h; + (*handler)(sig, sc); +} static void change_signals(int type) { diff -Naur -X exclude-files ac_cur/arch/um/kernel/syscall_kern.c ac/arch/um/kernel/syscall_kern.c --- ac_cur/arch/um/kernel/syscall_kern.c Fri Oct 5 14:43:33 2001 +++ ac/arch/um/kernel/syscall_kern.c Fri Oct 5 15:01:48 2001 @@ -12,6 +12,7 @@ #include "linux/shm.h" #include "linux/sys.h" #include "linux/unistd.h" +#include "linux/slab.h" #include "asm/mman.h" #include "asm/uaccess.h" #include "asm/ipc.h" @@ -300,17 +301,8 @@ int sys_sigaltstack(const stack_t *uss, stack_t *uoss) { - unsigned long old_sp = current->sas_ss_sp; - int old_size = current->sas_ss_size, res; - - res = do_sigaltstack(uss, uoss, UM_SP(¤t->thread.process_regs)); - if((res == 0) && (current->sas_ss_sp != 0) && - ((current->sas_ss_sp != old_sp) || - (current->sas_ss_size != old_size))){ - res = setup_altstack(current->sas_ss_sp, - current->sas_ss_size); - } - return(res); + return(do_sigaltstack(uss, uoss, + UM_SP(¤t->thread.process_regs))); } int nsyscalls = 0; diff -Naur -X exclude-files ac_cur/arch/um/kernel/syscall_user.c ac/arch/um/kernel/syscall_user.c --- ac_cur/arch/um/kernel/syscall_user.c Fri Oct 5 14:15:35 2001 +++ ac/arch/um/kernel/syscall_user.c Fri Oct 5 15:01:48 2001 @@ -5,7 +5,6 @@ /* XXX FIXME : Ensure that SIGIO and SIGVTALRM can't happen immediately * after setting up syscall stack - * SIGIO and SIGVTALRM should block SIGUSR2 * block SIGVTALRM in any code that's under wait_for_stop */ @@ -26,6 +25,7 @@ #include "user_util.h" #include "kern_util.h" #include "user.h" +#include "signal_kern.h" #include "sysdep/ptrace.h" /* XXX Bogus */ @@ -43,20 +43,8 @@ int syscall_index = 0; -void process_stack_handler(int sig) -{ - signal_deliverer(); -} - -int alt_stack_handler(void *arg) -{ - ptrace(PTRACE_TRACEME, 0, 0, 0); - kill(getpid(), SIGSTOP); - signal_deliverer(); - return(0); -} - extern int timer_ready; +extern int signal_frame_size; int syscall_handler(void *unused) { @@ -90,7 +78,7 @@ syscall_record[index].result = result; gettimeofday(&syscall_record[index].end, NULL); ret_from_sys_call(NULL); - if(have_signals(NULL, 0)) probe_stack(UM_SP(regs)); + /* XXX * This is a race, set_user_thread has to be called with signals off */ @@ -99,15 +87,13 @@ return(0); } -int exit_kernel(int pid, void *task, int *signal_out) +int exit_kernel(int pid, void *task) { void *stack; struct sys_pt_regs *regs; - unsigned long sp; int tracing, again, n, restore; tracing = 1; - *signal_out = 0; again = get_repeat_syscall(task); set_repeat_syscall(0); restore = get_restore_regs(task); @@ -116,19 +102,11 @@ if(ptrace_setregs(pid, regs) < 0) tracer_panic("Couldn't restore registers"); } - if(have_signals(task, 0)){ - handling_signal(task); - if(!restore) ptrace_getregs(pid, regs); - *signal_out = SIGUSR2; - } - else if(have_signals(task, 1)){ - handling_signal(task); + if(have_signals(task)){ if(!restore) ptrace_getregs(pid, regs); - regs = altstack_state(task, &stack, &n); + regs = signal_state(task); if(ptrace_setregs(pid, regs) < 0) panic("Couldn't set alternate stack state"); - sp = um_virt_to_phys(task, UM_SP(regs)); - memcpy((void *) sp, stack, n); } else if(again){ regs = syscall_state(task, &stack, &n); diff -Naur -X exclude-files ac_cur/arch/um/kernel/time.c ac/arch/um/kernel/time.c --- ac_cur/arch/um/kernel/time.c Fri Oct 5 14:15:35 2001 +++ ac/arch/um/kernel/time.c Fri Oct 5 15:01:48 2001 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -16,6 +16,7 @@ #include "kern_util.h" #include "user.h" #include "process.h" +#include "signal_user.h" extern struct timeval xtime; @@ -60,8 +61,7 @@ if(signal(SIGVTALRM, SIG_IGN) == SIG_ERR) panic("Couldn't unset SIGVTALRM handler"); set_handler(SIGALRM, (__sighandler_t) alarm_handler, - SA_NODEFER | SA_RESTART, - SIGUSR1, SIGIO, SIGUSR2, -1); + SA_NODEFER | SA_RESTART, SIGUSR1, SIGIO, -1); set_interval(ITIMER_REAL); } diff -Naur -X exclude-files ac_cur/arch/um/kernel/trap_kern.c ac/arch/um/kernel/trap_kern.c --- ac_cur/arch/um/kernel/trap_kern.c Fri Oct 5 14:43:33 2001 +++ ac/arch/um/kernel/trap_kern.c Fri Oct 5 15:01:48 2001 @@ -49,20 +49,6 @@ else if(expand_stack(vma, address)) ok = 0; } if(!ok){ - if(current->thread.signal.state == SIGNAL_HANDLING){ - /* The signal delivery failed because the SIGUSR2 - * frame couldn't be constructed - */ - current->thread.signal.state = SIGNAL_NONE; - if(current->thread.signal.signal == SIGSEGV){ - struct k_sigaction *ka; - - ka = ¤t->sig->action[SIGSEGV - 1]; - ka->sa.sa_handler = SIG_DFL; - } - force_sig(SIGSEGV, current); - return(0); - } if (current->thread.fault_catcher != NULL) { current->thread.fault_addr = (void *) address; up_read(&mm->mmap_sem); diff -Naur -X exclude-files ac_cur/arch/um/kernel/trap_user.c ac/arch/um/kernel/trap_user.c --- ac_cur/arch/um/kernel/trap_user.c Fri Oct 5 14:51:24 2001 +++ ac/arch/um/kernel/trap_user.c Fri Oct 5 15:01:48 2001 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -102,6 +102,7 @@ int last_index, proc_id, save_errno = 0; setup_kernel_stack(); + setup_signal_stack(); calc_sigframe_size(); signal(SIGSEGV, signal_segv); signal(SIGUSR1, signal_usr1); @@ -183,7 +184,7 @@ case OP_SWITCH: continue; case OP_TRACE_ON: - tracing = exit_kernel(pid, task, &sig); + tracing = exit_kernel(pid, task); break; case OP_TRACE_OFF: tracing = 0; @@ -251,6 +252,7 @@ child_signal(pid, status); continue; } + tracing = 0; break; } set_tracing(task, tracing); @@ -324,6 +326,7 @@ { int user_mode, save_errno = errno, save_timer = timer_on; + unprotect_kernel_mem(); timer_on = 0; user_mode = user_context(SC_SP(&sc)); if(user_mode) timer_ready = 1; @@ -343,6 +346,7 @@ { int user_mode, save_errno = errno, save_timer = timer_on; + unprotect_kernel_mem(); timer_on = 0; user_mode = user_context(SC_SP(&sc)); if(user_mode) timer_ready = 1; diff -Naur -X exclude-files ac_cur/include/asm-um/processor-generic.h ac/include/asm-um/processor-generic.h --- ac_cur/include/asm-um/processor-generic.h Fri Oct 5 14:45:08 2001 +++ ac/include/asm-um/processor-generic.h Fri Oct 5 15:07:24 2001 @@ -11,9 +11,9 @@ struct task_struct; #include "linux/config.h" +#include "linux/signal.h" #include "asm/segment.h" #include "asm/ptrace.h" -#include "asm/arch/signal.h" struct mm_struct; @@ -21,7 +21,6 @@ #define SIGNAL_NONE 0 #define SIGNAL_PENDING 1 -#define SIGNAL_HANDLING 2 struct thread_struct { int extern_pid; @@ -31,16 +30,13 @@ struct { int state; int signal; - unsigned long sp; unsigned long handler; } signal; - sigset_t saved_sigs; + struct signal_context *signal_context; int nsyscalls; struct sys_pt_regs process_regs; struct sys_pt_regs syscall_regs; struct sys_pt_regs altstack_regs; - void *altstack; - int altstack_size; unsigned long cr2; int err; int repeat_syscall; @@ -110,16 +106,12 @@ signal: { \ state: SIGNAL_NONE, \ signal: 0, \ - sp: 0, \ handler:0 \ }, \ - saved_sigs: { { 0 } }, \ nsyscalls: 0, \ process_regs: EMPTY_REGS, \ syscall_regs: EMPTY_REGS, \ altstack_regs: EMPTY_REGS, \ - altstack: NULL, \ - altstack_size: 0, \ cr2: 0, \ err: 0, \ repeat_syscall: 0, \ diff -Naur -X exclude-files ac_cur/include/asm-um/signal.h ac/include/asm-um/signal.h --- ac_cur/include/asm-um/signal.h Fri Oct 5 14:45:08 2001 +++ ac/include/asm-um/signal.h Fri Oct 5 15:07:24 2001 @@ -1,6 +1,14 @@ #ifndef __UM_SIGNAL_H #define __UM_SIGNAL_H +#include "sysdep/ptrace.h" #include "asm/arch/signal.h" + +struct signal_context { + struct sys_pt_regs regs; + sigset_t sigs; + int repeat; + struct signal_context *prev; +}; #endif