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 void sys_sigreturn(int signr, unsigned long oldmask, unsigned long unused)
     /* [previous][next][first][last][top][bottom][index][help] */
 147 {
 148         current->blocked = oldmask & _BLOCKABLE;
 149 }
 150 
 151 /*
 152  * This routine sets up the return stack for the first signal found
 153  * (== last delivered). It makes room for the registers we need to save,
 154  * but the actual saving is left until the very last moment when we
 155  * know whether we can restart system calls etc.
 156  */
 157 static unsigned long * setup_first(struct pt_regs * regs,
     /* [previous][next][first][last][top][bottom][index][help] */
 158         int signr, unsigned long sa_handler, unsigned long oldmask)
 159 {
 160         unsigned long * tmp_esp;
 161 
 162         regs->esp -= 18*4;
 163         tmp_esp = (unsigned long *) regs->esp;
 164         verify_area(VERIFY_WRITE,tmp_esp,18*4);
 165 /* set up the "normal" stack seen by the signal handler */
 166         put_fs_long(regs->esp+15*4,tmp_esp);    /* points to the stack.. */
 167         put_fs_long(signr,tmp_esp+1);           /* parameter to handler and sigreturn */
 168         put_fs_long(0,tmp_esp+2);               /* third parameter to sigreturn */
 169         put_fs_long(oldmask,tmp_esp+3);         /* second .. */
 170         put_fs_long(__NR_sigreturn,tmp_esp+4);  /* sigreturn number.. */
 171 /* save this frame so that we later can fill in the saved registers */
 172         return tmp_esp+5;
 173 }
 174 
 175 /*
 176  * This sets up the stack for any stacked signals other than the
 177  * first one: no need to restore registers etc, as that is done
 178  * by the very last signal handler return code..
 179  */
 180 static void setup_other(unsigned long eip, struct pt_regs * regs, int signr,
     /* [previous][next][first][last][top][bottom][index][help] */
 181         unsigned long sa_handler, unsigned long oldmask)
 182 {
 183         unsigned long * tmp_esp;
 184 
 185         regs->esp -= 9*4;
 186         tmp_esp = (unsigned long *) regs->esp;
 187         verify_area(VERIFY_WRITE,tmp_esp,9*4);
 188 /* set up the "normal" stack seen by the signal handler */
 189         put_fs_long(regs->esp+6*4,tmp_esp);     /* points to the stack.. */
 190         put_fs_long(signr,tmp_esp+1);           /* parameter to handler and sigreturn */
 191         put_fs_long(0,tmp_esp+2);               /* third parameter to sigreturn */
 192         put_fs_long(oldmask,tmp_esp+3);         /* second .. */
 193         put_fs_long(__NR_sigreturn,tmp_esp+4);  /* sigreturn number.. */
 194         put_fs_long(eip,tmp_esp+5);             /* return address */
 195 /* set up the return code... */
 196         put_fs_long(0x58595a5b,tmp_esp+6);      /* pop bx,dx,cx,ax */
 197         put_fs_long(0x909080cd,tmp_esp+7);      /* int $0x80 + nop + nop */
 198         put_fs_long(0x000cc290,tmp_esp+8);      /* nop + "ret 12" */
 199 }
 200 
 201 /*
 202  * Note that 'init' is a special process: it doesn't get signals it doesn't
 203  * want to handle. Thus you cannot kill init even with a SIGKILL even by
 204  * mistake.
 205  */
 206 int do_signal(unsigned long oldmask, struct pt_regs * regs)
     /* [previous][next][first][last][top][bottom][index][help] */
 207 {
 208         unsigned long *frame = NULL;
 209         unsigned long eip = 0;
 210         unsigned long signr;
 211         unsigned long sa_handler;
 212         struct sigaction * sa;
 213 
 214         while ((signr = current->signal & ~current->blocked)) {
 215                 __asm__("bsf %2,%1\n\t"
 216                         "btrl %1,%0"
 217                         :"=m" (current->signal),"=r" (signr)
 218                         :"1" (signr));
 219                 sa = current->sigaction + signr;
 220                 signr++;
 221                 sa_handler = (unsigned long) sa->sa_handler;
 222                 if (sa_handler==1) {
 223 /* check for SIGCHLD: it's special */
 224                         if (signr == SIGCHLD)
 225                                 while (sys_waitpid(-1,NULL,WNOHANG) > 0)
 226                                         /* nothing */;
 227                         continue;
 228                 }
 229                 if (!sa_handler) {
 230                         if (current->pid == 1)
 231                                 continue;
 232                         switch (signr) {
 233                         case SIGCONT: case SIGCHLD: case SIGWINCH:
 234                                 continue;
 235 
 236                         case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
 237                                 current->state = TASK_STOPPED;
 238                                 current->exit_code = signr;
 239                                 if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & 
 240                                                 SA_NOCLDSTOP))
 241                                         send_sig(SIGCHLD, current->p_pptr, 1);
 242                                 schedule();
 243                                 continue;
 244 
 245                         case SIGQUIT: case SIGILL: case SIGTRAP:
 246                         case SIGIOT: case SIGFPE: case SIGSEGV:
 247                                 if (core_dump(signr,regs))
 248                                         signr |= 0x80;
 249                                 /* fall through */
 250                         default:
 251                                 current->signal |= _S(signr & 0x7f);
 252                                 do_exit(signr);
 253                         }
 254                 }
 255                 /*
 256                  * OK, we're invoking a handler
 257                  */
 258                 if (regs->orig_eax >= 0) {
 259                         if (regs->eax == -ERESTARTNOHAND ||
 260                            (regs->eax == -ERESTARTSYS && (sa->sa_flags & SA_INTERRUPT)))
 261                                 regs->eax = -EINTR;
 262                 }
 263                 if (sa->sa_flags & SA_ONESHOT)
 264                         sa->sa_handler = NULL;
 265 /* force a supervisor-mode page-in of the signal handler to reduce races */
 266                 __asm__("testb $0,%%fs:%0"::"m" (*(char *) sa_handler));
 267                 if (!frame) {
 268                         frame = setup_first(regs,signr,sa_handler,oldmask);
 269                 } else
 270                         setup_other(eip,regs,signr,sa_handler,oldmask);
 271                 eip = sa_handler;
 272                 current->blocked |= sa->sa_mask;
 273                 oldmask |= sa->sa_mask;
 274         }
 275         if (regs->orig_eax >= 0 &&
 276             (regs->eax == -ERESTARTNOHAND ||
 277              regs->eax == -ERESTARTSYS ||
 278              regs->eax == -ERESTARTNOINTR)) {
 279                 regs->eax = regs->orig_eax;
 280                 regs->eip -= 2;
 281         }
 282         if (!frame)                             /* no handlers installed - return 0 */
 283                 return 0;
 284 /* save registers if one or more handlers are called.. */
 285         put_fs_long(regs->edi,frame);           /* suitable order for "popad" */
 286         put_fs_long(regs->esi,frame+1);
 287         put_fs_long(regs->ebp,frame+2);         /* using 'frame++' instead of the 'frame+x' */
 288         put_fs_long(regs->esp,frame+3);         /* form used now results in atrocious code */
 289         put_fs_long(regs->ebx,frame+4);         /* due to gcc not being very good at optimizing */
 290         put_fs_long(regs->edx,frame+5);         /* things with inline-assembly/functions.. */
 291         put_fs_long(regs->ecx,frame+6);
 292         put_fs_long(regs->eax,frame+7);
 293         put_fs_long(regs->eflags,frame+8);      /* flags */
 294         put_fs_long(regs->eip,frame+9);         /* original return address */
 295 /* set up the return code... */
 296         put_fs_long(0x58595a5b,frame+10);       /* pop bx,dx,cx,ax */
 297         put_fs_long(0x906180cd,frame+11);       /* int $0x80 + popad + nop */
 298         put_fs_long(0x000cc29d,frame+12);       /* popfl + "ret 12" */
 299         regs->eip = eip;                        /* "return" to the first handler */
 300         return 1;
 301 }

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