Messages in this thread Patch in this message |  | | From | Melvin Smith <> | Subject | PATCH for signal stacks, sigaltstack() call #1 | Date | Fri, 24 May 1996 23:31:27 -0400 (EDT) |
| |
I implemented signal stacks for pre2.0.6, and have tested successfully with single threaded, single process. This is the beginning of reliable sigs, SA_SIGINFO, etc. For example, with the patch, you can now handle a stack overflow correctly with SIGSEGV. The patch is included the next mailing will be a test program if you want to play with it. Some of the code is #ifdef 0 such as the fork() and clone() related because it is untested. This should go into pre2.0 because IMHO it is not a NEW feature but plugging a hole, and Linux is about the only flavor without support.
I would appreciate if someone would play with the test program and give me some report.
-Melvin Smith
diff -urN /usr/src/linux-pre2.0.6/arch/i386/kernel/entry.S /usr/src/linux/arch/i386/kernel/entry.S --- /usr/src/linux-pre2.0.6/arch/i386/kernel/entry.S Mon May 13 16:23:08 1996 +++ /usr/src/linux/arch/i386/kernel/entry.S Mon May 20 23:24:10 1996 @@ -674,4 +674,5 @@ .long SYMBOL_NAME(sys_sched_rr_get_interval) .long SYMBOL_NAME(sys_nanosleep) .long SYMBOL_NAME(sys_mremap) - .space (NR_syscalls-163)*4 + .long SYMBOL_NAME(sys_sigaltstack) + .space (NR_syscalls-164)*4 diff -urN /usr/src/linux-pre2.0.6/arch/i386/kernel/process.c /usr/src/linux/arch/i386/kernel/process.c --- /usr/src/linux-pre2.0.6/arch/i386/kernel/process.c Fri Apr 19 11:35:15 1996 +++ /usr/src/linux/arch/i386/kernel/process.c Fri May 24 23:08:47 1996 @@ -267,6 +267,12 @@ for (i=0 ; i<8 ; i++) current->debugreg[i] = 0; + /* zero sigaltstack */ + if( current->sa_info ) { + kfree( current->sa_info ); + current->sa_info = 0; + } + /* * Forget coprocessor state.. */ @@ -309,6 +315,25 @@ *childregs = *regs; childregs->eax = 0; childregs->esp = esp; + +#ifdef 0 + /* UNTESTED!! - Melvin */ + /* Here we need to check if parent was on sigstack + * If so, start child off on its sigstack which has already + * been cloned by copy_sighand() + */ + +#define CUR_ALT_STACK_BOT (unsigned long)current->sa_info->ss_sp +#define CUR_ALT_STACK_TOP CUR_ALT_STACK_BOT - ( current->sa_info->ss_size - 1 ) + + if( current->sa_info && current->tss.esp < CUR_ALT_STACK_BOT + && current->tss.esp > CUR_ALT_STACK_TOP ) { + + /* Duplicate sig-stack frame position */ + /* set SA_ONSTACK bit */ + } +#endif + p->tss.back_link = 0; p->tss.eflags = regs->eflags & 0xffffcfff; /* iopl is always 0 for a new process */ p->tss.ldt = _LDT(nr); diff -urN /usr/src/linux-pre2.0.6/arch/i386/kernel/signal.c /usr/src/linux/arch/i386/kernel/signal.c --- /usr/src/linux-pre2.0.6/arch/i386/kernel/signal.c Mon May 6 09:31:18 1996 +++ /usr/src/linux/arch/i386/kernel/signal.c Fri May 24 23:07:10 1996 @@ -2,6 +2,10 @@ * linux/arch/i386/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds + * + * 05.25.96 Melvin Smith + * Started reliable signal handling. Signal stacks implemented. + * Hopefully SA_SIGINFO is next. */ #include <linux/config.h> @@ -11,6 +15,7 @@ #include <linux/kernel.h> #include <linux/signal.h> #include <linux/errno.h> +#include <linux/malloc.h> #include <linux/wait.h> #include <linux/ptrace.h> #include <linux/unistd.h> @@ -72,10 +77,83 @@ restore_i387_soft(buf); #endif } - + +/* Right now I'm checking not only instack but instack->ss_sp, maybe wrong + * but I think it is safe. System shouldn't let user install stack if it + * is too small anyway + */ +asmlinkage int sys_sigaltstack( const struct sigaltstack * sa_in, + struct sigaltstack *sa_out ) +{ + /* In case sa_in == sa_out */ + struct sigaltstack sa_temp, sa_new; + + /* If old sigstack pointer then save old info in that pointer */ + if( sa_out ) { + if( verify_area( VERIFY_WRITE, sa_out, sizeof( struct sigaltstack ) ) ) + return -EINVAL; + + if( !current->sa_info ) + memset( &sa_temp, 0, sizeof( struct sigaltstack ) ); + else + memcpy( &sa_temp, current->sa_info, sizeof( struct sigaltstack ) ); + } + + /* If new sigstack is passed then update current */ + sa_new.ss_sp = 0; + if( sa_in ) { + if( verify_area( VERIFY_READ, sa_in, sizeof( struct sigaltstack ) ) ) + return -EINVAL; + memcpy_fromfs( &sa_new, sa_in, sizeof( *sa_in ) ); + } + + if( sa_new.ss_sp ) { + /* If SS_DISABLE, ignore stack info */ + if( !( sa_new.ss_flags & SS_DISABLE ) ) { + /* Can't change stack while executing on that stack */ + if( current->sa_info && (current->sa_info->ss_flags & SA_ONSTACK) ) + return -EINVAL; + + /* Make sure the stack is as big as the user says it is, either + * way it must be at least MINSIGSTKSZ bytes */ + if( sa_new.ss_size < MINSIGSTKSZ ) + return -EINVAL; + else if( verify_area( VERIFY_WRITE, sa_new.ss_sp, sa_new.ss_size ) ) + return -EINVAL; + } + + if( !current->sa_info ) { + current->sa_info = (struct sigaltstack *)kmalloc( sizeof( struct sigaltstack ), + GFP_KERNEL ); + if( !current->sa_info ) + return -ENOMEM; + } + + memcpy( current->sa_info, &sa_new, sizeof( struct sigaltstack ) ); + /* i386 stack starts at high mem. + * This is transparent to caller, reset if sigaltstack queried. + */ + (char *)current->sa_info->ss_sp += ( current->sa_info->ss_size - 1 ); + } + else if( current->sa_info ) { + /* sa_in.ss_sp = 0 so we remove sig-stack from task struct */ + kfree( current->sa_info ); + current->sa_info = 0; + } + + if( sa_out ) { + /* Set back to low memory so user can handle it like normal. */ + if( sa_temp.ss_sp ) + (char *)sa_temp.ss_sp -= ( sa_temp.ss_size - 1 ); + memcpy_tofs( sa_out, &sa_temp, sizeof( struct sigaltstack ) ); + } + + return 0; +} + /* - * This sets regs->esp even though we don't actually use sigstacks yet.. + * 5.25.96 Added sigaltstack restore */ asmlinkage int sys_sigreturn(unsigned long __unused) { @@ -92,6 +170,7 @@ goto badframe; memcpy_fromfs(&context,(void *) regs->esp, sizeof(context)); current->blocked = context.oldmask & _BLOCKABLE; + COPY_SEG(ds); COPY_SEG(es); COPY_SEG(fs); @@ -106,6 +185,18 @@ regs->eflags &= ~0x40DD5; regs->eflags |= context.eflags & 0x40DD5; regs->orig_eax = -1; /* disable syscall checks */ + +#define CUR_ALT_STACK_BOT (unsigned long)current->sa_info->ss_sp +#define CUR_ALT_STACK_TOP CUR_ALT_STACK_BOT - ((unsigned long)current->sa_info->ss_sp - 1 ) + + /* Before clearing altstack bit, test if esp still within altstack + * space. A nested signal handler on alt stack maybe */ + if( current->sa_info && ( current->sa_info->ss_flags & SS_ONSTACK ) ) { + if( regs->esp <= CUR_ALT_STACK_BOT + || regs->esp >= CUR_ALT_STACK_TOP ) + current->sa_info->ss_flags &= ~SS_ONSTACK; + } + if (context.fpstate) { struct _fpstate * buf = context.fpstate; if (verify_area(VERIFY_READ, buf, sizeof(*buf))) @@ -156,6 +247,9 @@ /* * Set up a signal frame... Make the stack look the way iBCS2 expects * it to look. + * 5.17.96 Added sigstack support. DEC UNIX manpage says if longjmp() is + * called from a -nested- signal on an alternate stack then the effect is + * undefined. I try to handle it here. */ static void setup_frame(struct sigaction * sa, struct pt_regs * regs, int signr, @@ -164,10 +258,19 @@ unsigned long * frame; frame = (unsigned long *) regs->esp; - if (regs->ss != USER_DS && sa->sa_restorer) - frame = (unsigned long *) sa->sa_restorer; + + /* See if sigaction requests alternate stack... + * Now see if we are already on that stack, or the stack is disabled. + */ + if (regs->ss == USER_DS && sa->sa_flags & SA_ONSTACK + && current->sa_info + && !( current->sa_info->ss_flags & (SS_DISABLE | SS_ONSTACK) ) ) { + frame = (unsigned long *) current->sa_info->ss_sp; + current->sa_info->ss_flags = SS_ONSTACK; + } + frame -= 64; - if (verify_area(VERIFY_WRITE,frame,64*4)) + if (verify_area(VERIFY_WRITE,frame,64*sizeof(*frame))) do_exit(SIGSEGV); /* set up the "normal" stack seen by the signal handler (iBCS2) */ diff -urN /usr/src/linux-pre2.0.6/include/asm-i386/signal.h /usr/src/linux/include/asm-i386/signal.h --- /usr/src/linux-pre2.0.6/include/asm-i386/signal.h Fri Mar 1 00:50:56 1996 +++ /usr/src/linux/include/asm-i386/signal.h Thu May 23 23:46:29 1996 @@ -44,17 +44,17 @@ #define SIGUNUSED 31 /* - * sa_flags values: SA_STACK is not currently supported, but will allow the - * usage of signal stacks by using the (now obsolete) sa_restorer field in - * the sigaction structure as a stack pointer. This is now possible due to - * the changes in signal handling. LBT 010493. + * SA_STACK changed to SA_ONSTACK and implementation started. + * The stack pointer is not on the sigact struct however, I don't think it + * belongs there but I may be wrong. I added sigaltstack struct and added + * a sigaltstack member to task_struct - 5.17.96 Melvin Smith * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_SHIRQ flag is for shared interrupt support on PCI and EISA. */ #define SA_NOCLDSTOP 1 #define SA_SHIRQ 0x04000000 -#define SA_STACK 0x08000000 +#define SA_ONSTACK 0x08000000 #define SA_RESTART 0x10000000 #define SA_INTERRUPT 0x20000000 #define SA_NOMASK 0x40000000 @@ -89,6 +89,21 @@ unsigned long sa_flags; void (*sa_restorer)(void); }; + +#define MINSIGSTKSZ 4096 /* minimum signal handler alternate stack size */ +#define SIGSTKSZ 16384 /* average or default alternate stack size */ + +#define SS_DISABLE 1 /* alternate stack disabled */ +#define SS_ONSTACK 2 /* process is currently executing on the alternate stack */ + +/* Used by sigaltstack() call */ +struct sigaltstack { + char * ss_sp; + unsigned long ss_flags; + long ss_size; +}; + +typedef struct sigaltstack stack_t; #ifdef __KERNEL__ #include <asm/sigcontext.h> diff -urN /usr/src/linux-pre2.0.6/include/asm-i386/unistd.h /usr/src/linux/include/asm-i386/unistd.h --- /usr/src/linux-pre2.0.6/include/asm-i386/unistd.h Fri Mar 22 01:34:02 1996 +++ /usr/src/linux/include/asm-i386/unistd.h Mon May 20 21:07:46 1996 @@ -169,6 +169,7 @@ #define __NR_sched_rr_get_interval 161 #define __NR_nanosleep 162 #define __NR_mremap 163 +#define __NR_sigaltstack 164 /* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */ #define _syscall0(type,name) \ diff -urN /usr/src/linux-pre2.0.6/include/linux/sched.h /usr/src/linux/include/linux/sched.h --- /usr/src/linux-pre2.0.6/include/linux/sched.h Sun May 19 09:27:11 1996 +++ /usr/src/linux/include/linux/sched.h Fri May 24 19:43:55 1996 @@ -244,6 +244,8 @@ struct mm_struct *mm; /* signal handlers */ struct signal_struct *sig; +/* signal stack */ + struct sigaltstack *sa_info; #ifdef __SMP__ int processor; int last_processor; @@ -309,6 +311,7 @@ /* files */ &init_files, \ /* mm */ &init_mm, \ /* signals */ &init_signals, \ +/* sig stack */ 0, \ } extern struct mm_struct init_mm; diff -urN /usr/src/linux-pre2.0.6/kernel/fork.c /usr/src/linux/kernel/fork.c --- /usr/src/linux-pre2.0.6/kernel/fork.c Mon Apr 22 06:08:47 1996 +++ /usr/src/linux/kernel/fork.c Fri May 24 23:10:37 1996 @@ -182,6 +182,9 @@ static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk) { if (clone_flags & CLONE_SIGHAND) { + /* This goes for sig-stack as well. It doesn't make sense to + * me to clone the sig-stack but what else to do? -Melvin + */ current->sig->count++; return 0; } @@ -190,6 +193,29 @@ return -1; tsk->sig->count = 1; memcpy(tsk->sig->action, current->sig->action, sizeof(tsk->sig->action)); + +#ifdef 0 + /* UNTESTED!! -Melvin */ + /* Needs more work so right now a fork() won't copy sig-stack */ + + /* Create an alternate sig-stack for new process */ + if( current->sa_info ) { + tsk->sa_info = (struct sigaltstack *)kmalloc( sizeof( struct sigaltstack ), GFP_KERNEL ); + if( !tsk->sa_info ) + return -ENOMEM; + tsk->sa_info->ss_size = current->sa_info->ss_size; + tsk->sa_info->ss_sp = (struct sigaltstack *)kmalloc( current->sa_info->ss_size, GFP_KERNEL ); + if( !tsk->sa_info->ss_sp ) { + kfree( tsk->sa_info ); + tsk->sa_info = 0; + return -ENOMEM; + } + + /* What if fork() is called while on sig-stack ? */ + tsk->sa_info->ss_flags = 0; + } +#endif + return 0; }
|  |