root/kernel/signal.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sys_sgetmask
  2. sys_ssetmask
  3. sys_sigpending
  4. sys_sigsuspend
  5. check_pending
  6. sys_signal
  7. sys_sigaction
  8. sys_sigreturn
  9. setup_first
  10. setup_other
  11. do_signal

   1 /*
   2  *  linux/kernel/signal.c
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  */
   6 
   7 #include <linux/sched.h>
   8 #include <linux/kernel.h>
   9 #include <linux/signal.h>
  10 #include <linux/errno.h>
  11 #include <linux/wait.h>
  12 #include <linux/ptrace.h>
  13 #include <linux/unistd.h>
  14 
  15 #include <asm/segment.h>
  16 
  17 #define _S(nr) (1<<((nr)-1))
  18 
  19 #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
  20 
  21 extern int core_dump(long signr,struct pt_regs * regs);
  22 int do_signal(unsigned long oldmask, struct pt_regs * regs);
  23 
  24 int sys_sgetmask(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  25 {
  26         return current->blocked;
  27 }
  28 
  29 int sys_ssetmask(int newmask)
     /* [previous][next][first][last][top][bottom][index][help] */
  30 {
  31         int old=current->blocked;
  32 
  33         current->blocked = newmask & _BLOCKABLE;
  34         return old;
  35 }
  36 
  37 int sys_sigpending(sigset_t *set)
     /* [previous][next][first][last][top][bottom][index][help] */
  38 {
  39         int error;
  40         /* fill in "set" with signals pending but blocked. */
  41         error = verify_area(VERIFY_WRITE, set, 4);
  42         if (!error)
  43                 put_fs_long(current->blocked & current->signal, (unsigned long *)set);
  44         return error;
  45 }
  46 
  47 /*
  48  * atomically swap in the new signal mask, and wait for a signal.
  49  */
  50 int sys_sigsuspend(volatile int restart, volatile unsigned long oldmask, unsigned long set)
     /* [previous][next][first][last][top][bottom][index][help] */
  51 {
  52         unsigned long mask;
  53         struct pt_regs * regs = (struct pt_regs *) &restart;
  54 
  55         mask = current->blocked;
  56         current->blocked = set & _BLOCKABLE;
  57         regs->eax = -EINTR;
  58         while (1) {
  59                 current->state = TASK_INTERRUPTIBLE;
  60                 schedule();
  61                 if (do_signal(mask,regs))
  62                         return -EINTR;
  63         }
  64 }
  65 
  66 /*
  67  * POSIX 3.3.1.3:
  68  *  "Setting a signal action to SIG_IGN for a signal that is pending
  69  *   shall cause the pending signal to be discarded, whether or not
  70  *   it is blocked" (but SIGCHLD is unspecified: linux leaves it alone).
  71  *
  72  *  "Setting a signal action to SIG_DFL for a signal that is pending
  73  *   and whose default action is to ignore the signal (for example,
  74  *   SIGCHLD), shall cause the pending signal to be discarded, whether
  75  *   or not it is blocked"
  76  *
  77  * Note the silly behaviour of SIGCHLD: SIG_IGN means that the signal
  78  * isn't actually ignored, but does automatic child reaping, while
  79  * SIG_DFL is explicitly said by POSIX to force the signal to be ignored..
  80  */
  81 static void check_pending(int signum)
     /* [previous][next][first][last][top][bottom][index][help] */
  82 {
  83         struct sigaction *p;
  84 
  85         p = signum - 1 + current->sigaction;
  86         if (p->sa_handler == SIG_IGN) {
  87                 if (signum == SIGCHLD)
  88                         return;
  89                 current->signal &= ~_S(signum);
  90                 return;
  91         }
  92         if (p->sa_handler == SIG_DFL) {
  93                 if (signum != SIGCONT && signum != SIGCHLD && signum != SIGWINCH)
  94                         return;
  95                 current->signal &= ~_S(signum);
  96                 return;
  97         }       
  98 }
  99 
 100 int sys_signal(int signum, long handler)
     /* [previous][next][first][last][top][bottom][index][help] */
 101 {
 102         struct sigaction tmp;
 103 
 104         if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
 105                 return -EINVAL;
 106         tmp.sa_handler = (void (*)(int)) handler;
 107         tmp.sa_mask = 0;
 108         tmp.sa_flags = SA_ONESHOT | SA_NOMASK | SA_INTERRUPT;
 109         tmp.sa_restorer = NULL;
 110         handler = (long) current->sigaction[signum-1].sa_handler;
 111         current->sigaction[signum-1] = tmp;
 112         check_pending(signum);
 113         return handler;
 114 }
 115 
 116 int sys_sigaction(int signum, const struct sigaction * action,
     /* [previous][next][first][last][top][bottom][index][help] */
 117         struct sigaction * oldaction)
 118 {
 119         struct sigaction new, *p;
 120 
 121         if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
 122                 return -EINVAL;
 123         p = signum - 1 + current->sigaction;
 124         if (action) {
 125                 memcpy_fromfs(&new, action, sizeof(struct sigaction));
 126                 if (new.sa_flags & SA_NOMASK)
 127                         new.sa_mask = 0;
 128                 else {
 129                         new.sa_mask |= _S(signum);
 130                         new.sa_mask &= _BLOCKABLE;
 131                 }
 132         }
 133         if (oldaction) {
 134                 if (!verify_area(VERIFY_WRITE,oldaction, sizeof(struct sigaction)))
 135                         memcpy_tofs(oldaction, p, sizeof(struct sigaction));
 136         }
 137         if (action) {
 138                 *p = new;
 139                 check_pending(signum);
 140         }
 141         return 0;
 142 }
 143 
 144 extern int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
 145 
 146 /*
 147  * This sets regs->esp even though we don't actually use sigstacks yet..
 148  */
 149 int sys_sigreturn(int signr, unsigned long oldmask, unsigned long esp)
     /* [previous][next][first][last][top][bottom][index][help] */
 150 {
 151         struct pt_regs * regs;
 152 
 153         regs = (struct pt_regs *) &signr;
 154         current->blocked = oldmask & _BLOCKABLE;
 155         regs->esp = esp;
 156         return 0;
 157 }
 158 
 159 /*
 160  * This routine sets up the return stack for the first signal found
 161  * (== last delivered). It makes room for the registers we need to save,
 162  * but the actual saving is left until the very last moment when we
 163  * know whether we can restart system calls etc.
 164  */
 165 static unsigned long * setup_first(struct pt_regs * regs,
     /* [previous][next][first][last][top][bottom][index][help] */
 166         int signr, unsigned long sa_handler, unsigned long oldmask)
 167 {
 168         unsigned long * tmp_esp;
 169 
 170         regs->esp -= 18*4;
 171         tmp_esp = (unsigned long *) regs->esp;
 172         verify_area(VERIFY_WRITE,tmp_esp,18*4);
 173 /* set up the "normal" stack seen by the signal handler */
 174         put_fs_long(regs->esp+15*4,tmp_esp);    /* points to the stack.. */
 175         put_fs_long(signr,tmp_esp+1);           /* parameter to handler and sigreturn */
 176         put_fs_long((unsigned long) (tmp_esp+5),tmp_esp+2);
 177         put_fs_long(oldmask,tmp_esp+3);         /* second .. */
 178         put_fs_long(__NR_sigreturn,tmp_esp+4);  /* sigreturn number.. */
 179 /* save this frame so that we later can fill in the saved registers */
 180         return tmp_esp+5;
 181 }
 182 
 183 /*
 184  * This sets up the stack for any stacked signals other than the
 185  * first one: no need to restore registers etc, as that is done
 186  * by the very last signal handler return code..
 187  */
 188 static void setup_other(unsigned long eip, struct pt_regs * regs, int signr,
     /* [previous][next][first][last][top][bottom][index][help] */
 189         unsigned long sa_handler, unsigned long oldmask)
 190 {
 191         unsigned long * tmp_esp;
 192 
 193         regs->esp -= 9*4;
 194         tmp_esp = (unsigned long *) regs->esp;
 195         verify_area(VERIFY_WRITE,tmp_esp,9*4);
 196 /* set up the "normal" stack seen by the signal handler */
 197         put_fs_long(regs->esp+6*4,tmp_esp);     /* points to the stack.. */
 198         put_fs_long(signr,tmp_esp+1);           /* parameter to handler and sigreturn */
 199         put_fs_long((unsigned long) (tmp_esp+5),tmp_esp+2);
 200         put_fs_long(oldmask,tmp_esp+3);         /* second .. */
 201         put_fs_long(__NR_sigreturn,tmp_esp+4);  /* sigreturn number.. */
 202         put_fs_long(eip,tmp_esp+5);             /* return address */
 203 /* set up the return code... */
 204         put_fs_long(0x58595a5b,tmp_esp+6);      /* pop bx,dx,cx,ax */
 205         put_fs_long(0x909080cd,tmp_esp+7);      /* int $0x80 + nop + nop */
 206         put_fs_long(0x000cc290,tmp_esp+8);      /* nop + "ret 12" */
 207 }
 208 
 209 /*
 210  * Note that 'init' is a special process: it doesn't get signals it doesn't
 211  * want to handle. Thus you cannot kill init even with a SIGKILL even by
 212  * mistake.
 213  */
 214 int do_signal(unsigned long oldmask, struct pt_regs * regs)
     /* [previous][next][first][last][top][bottom][index][help] */
 215 {
 216         unsigned long *frame = NULL;
 217         unsigned long eip = 0;
 218         unsigned long signr;
 219         unsigned long sa_handler;
 220         struct sigaction * sa;
 221 
 222         while ((signr = current->signal & ~current->blocked)) {
 223                 __asm__("bsf %2,%1\n\t"
 224                         "btrl %1,%0"
 225                         :"=m" (current->signal),"=r" (signr)
 226                         :"1" (signr));
 227                 sa = current->sigaction + signr;
 228                 signr++;
 229                 sa_handler = (unsigned long) sa->sa_handler;
 230                 if (sa_handler==1) {
 231 /* check for SIGCHLD: it's special */
 232                         if (signr == SIGCHLD)
 233                                 while (sys_waitpid(-1,NULL,WNOHANG) > 0)
 234                                         /* nothing */;
 235                         continue;
 236                 }
 237                 if (!sa_handler) {
 238                         if (current->pid == 1)
 239                                 continue;
 240                         switch (signr) {
 241                         case SIGCONT: case SIGCHLD: case SIGWINCH:
 242                                 continue;
 243 
 244                         case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
 245                                 current->state = TASK_STOPPED;
 246                                 current->exit_code = signr;
 247                                 if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & 
 248                                                 SA_NOCLDSTOP))
 249                                         send_sig(SIGCHLD, current->p_pptr, 1);
 250                                 schedule();
 251                                 continue;
 252 
 253                         case SIGQUIT: case SIGILL: case SIGTRAP:
 254                         case SIGIOT: case SIGFPE: case SIGSEGV:
 255                                 if (core_dump(signr,regs))
 256                                         signr |= 0x80;
 257                                 /* fall through */
 258                         default:
 259                                 current->signal |= _S(signr & 0x7f);
 260                                 do_exit(signr);
 261                         }
 262                 }
 263                 /*
 264                  * OK, we're invoking a handler
 265                  */
 266                 if (regs->orig_eax >= 0) {
 267                         if (regs->eax == -ERESTARTNOHAND ||
 268                            (regs->eax == -ERESTARTSYS && (sa->sa_flags & SA_INTERRUPT)))
 269                                 regs->eax = -EINTR;
 270                 }
 271                 if (sa->sa_flags & SA_ONESHOT)
 272                         sa->sa_handler = NULL;
 273 /* force a supervisor-mode page-in of the signal handler to reduce races */
 274                 __asm__("testb $0,%%fs:%0"::"m" (*(char *) sa_handler));
 275                 if (!frame) {
 276                         frame = setup_first(regs,signr,sa_handler,oldmask);
 277                 } else
 278                         setup_other(eip,regs,signr,sa_handler,oldmask);
 279                 eip = sa_handler;
 280                 current->blocked |= sa->sa_mask;
 281                 oldmask |= sa->sa_mask;
 282         }
 283         if (regs->orig_eax >= 0 &&
 284             (regs->eax == -ERESTARTNOHAND ||
 285              regs->eax == -ERESTARTSYS ||
 286              regs->eax == -ERESTARTNOINTR)) {
 287                 regs->eax = regs->orig_eax;
 288                 regs->eip -= 2;
 289         }
 290         if (!frame)                             /* no handlers installed - return 0 */
 291                 return 0;
 292 /* save registers if one or more handlers are called.. */
 293         put_fs_long(regs->edi,frame);           /* suitable order for "popad" */
 294         put_fs_long(regs->esi,frame+1);
 295         put_fs_long(regs->ebp,frame+2);         /* using 'frame++' instead of the 'frame+x' */
 296         put_fs_long(regs->esp,frame+3);         /* form used now results in atrocious code */
 297         put_fs_long(regs->ebx,frame+4);         /* due to gcc not being very good at optimizing */
 298         put_fs_long(regs->edx,frame+5);         /* things with inline-assembly/functions.. */
 299         put_fs_long(regs->ecx,frame+6);
 300         put_fs_long(regs->eax,frame+7);
 301         put_fs_long(regs->eflags,frame+8);      /* flags */
 302         put_fs_long(regs->eip,frame+9);         /* original return address */
 303 /* set up the return code... */
 304         put_fs_long(0x58595a5b,frame+10);       /* pop bx,dx,cx,ax */
 305         put_fs_long(0x906180cd,frame+11);       /* int $0x80 + popad + nop */
 306         put_fs_long(0x000cc29d,frame+12);       /* popfl + "ret 12" */
 307         regs->eip = eip;                        /* "return" to the first handler */
 308         return 1;
 309 }

/* [previous][next][first][last][top][bottom][index][help] */