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_frame
  10. 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(int restart, 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, unsigned 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         if (handler >= TASK_SIZE)
 107                 return -EFAULT;
 108         tmp.sa_handler = (void (*)(int)) handler;
 109         tmp.sa_mask = 0;
 110         tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
 111         tmp.sa_restorer = NULL;
 112         handler = (long) current->sigaction[signum-1].sa_handler;
 113         current->sigaction[signum-1] = tmp;
 114         check_pending(signum);
 115         return handler;
 116 }
 117 
 118 int sys_sigaction(int signum, const struct sigaction * action,
     /* [previous][next][first][last][top][bottom][index][help] */
 119         struct sigaction * oldaction)
 120 {
 121         struct sigaction new, *p;
 122 
 123         if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
 124                 return -EINVAL;
 125         p = signum - 1 + current->sigaction;
 126         if (action) {
 127                 memcpy_fromfs(&new, action, sizeof(struct sigaction));
 128                 if (new.sa_flags & SA_NOMASK)
 129                         new.sa_mask = 0;
 130                 else {
 131                         new.sa_mask |= _S(signum);
 132                         new.sa_mask &= _BLOCKABLE;
 133                 }
 134                 if (TASK_SIZE <= (unsigned long) new.sa_handler)
 135                         return -EFAULT;
 136         }
 137         if (oldaction) {
 138                 if (!verify_area(VERIFY_WRITE,oldaction, sizeof(struct sigaction)))
 139                         memcpy_tofs(oldaction, p, sizeof(struct sigaction));
 140         }
 141         if (action) {
 142                 *p = new;
 143                 check_pending(signum);
 144         }
 145         return 0;
 146 }
 147 
 148 extern int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
 149 
 150 /*
 151  * This sets regs->esp even though we don't actually use sigstacks yet..
 152  */
 153 int sys_sigreturn(unsigned long oldmask, unsigned long eip, unsigned long esp)
     /* [previous][next][first][last][top][bottom][index][help] */
 154 {
 155         struct pt_regs * regs;
 156 
 157         regs = (struct pt_regs *) &oldmask;
 158         current->blocked = oldmask & _BLOCKABLE;
 159         regs->eip = eip;
 160         regs->esp = esp;
 161         return 0;
 162 }
 163 
 164 /*
 165  * Set up a signal frame... Make the stack look the way iBCS2 expects
 166  * it to look.
 167  */
 168 static void setup_frame(unsigned long ** fp, unsigned long eip,
     /* [previous][next][first][last][top][bottom][index][help] */
 169         struct pt_regs * regs, int signr,
 170         unsigned long sa_handler, unsigned long oldmask)
 171 {
 172         unsigned long * frame;
 173 
 174 #define __CODE ((unsigned long)(frame+24))
 175 #define CODE(x) ((void *) ((x)+__CODE))
 176         frame = *fp - 32;
 177         verify_area(VERIFY_WRITE,frame,32*4);
 178 /* set up the "normal" stack seen by the signal handler (iBCS2) */
 179         put_fs_long(__CODE,frame);
 180         put_fs_long(signr, frame+1);
 181         put_fs_long(regs->gs, frame+2);
 182         put_fs_long(regs->fs, frame+3);
 183         put_fs_long(regs->es, frame+4);
 184         put_fs_long(regs->ds, frame+5);
 185         put_fs_long(regs->edi, frame+6);
 186         put_fs_long(regs->esi, frame+7);
 187         put_fs_long(regs->ebp, frame+8);
 188         put_fs_long(regs->esp, frame+9);
 189         put_fs_long(regs->ebx, frame+10);
 190         put_fs_long(regs->edx, frame+11);
 191         put_fs_long(regs->ecx, frame+12);
 192         put_fs_long(regs->eax, frame+13);
 193         put_fs_long(0, frame+14);               /* trapno */
 194         put_fs_long(0, frame+15);               /* err */
 195         put_fs_long(regs->eip, frame+16);
 196         put_fs_long(regs->cs, frame+17);
 197         put_fs_long(regs->eflags, frame+18);
 198         put_fs_long(regs->esp, frame+19);
 199         put_fs_long(regs->ss, frame+20);
 200         put_fs_long(0,frame+21);                /* 387 state pointer */
 201 /* linux extended stack - easier to handle.. */
 202         put_fs_long(regs->eflags, frame+22);
 203         put_fs_long(eip, frame+23);
 204 /* set up the return code... */
 205         put_fs_long(0x0000b858, CODE(0));       /* popl %eax ; movl $,%eax */
 206         put_fs_long(0x00bb0000, CODE(4));       /* movl $,%ebx */
 207         put_fs_long(0xcd000000, CODE(8));       /* int $0x80 */
 208         put_fs_long(0x0fa90f80, CODE(12));      /* pop %gs ; pop %fs */
 209         put_fs_long(0x611f07a1, CODE(16));      /* pop %es ; pop %ds ; popad */
 210         put_fs_long(0x20c48390, CODE(20));      /* nop ; addl $32,%esp */
 211         put_fs_long(0x0020c29d, CODE(24));      /* popfl ; ret $32 */
 212         put_fs_long(__NR_ssetmask, CODE(2));
 213         put_fs_long(oldmask, CODE(7));
 214         *fp = frame;
 215 #undef __CODE
 216 #undef CODE
 217 }
 218 
 219 /*
 220  * Note that 'init' is a special process: it doesn't get signals it doesn't
 221  * want to handle. Thus you cannot kill init even with a SIGKILL even by
 222  * mistake.
 223  *
 224  * Note that we go through the signals twice: once to check the signals that
 225  * the kernel can handle, and then we build all the user-level signal handling
 226  * stack-frames in one go after that.
 227  */
 228 int do_signal(unsigned long oldmask, struct pt_regs * regs)
     /* [previous][next][first][last][top][bottom][index][help] */
 229 {
 230         unsigned long mask = ~current->blocked;
 231         unsigned long handler_signal = 0;
 232         unsigned long *frame = NULL;
 233         unsigned long eip = 0;
 234         unsigned long signr;
 235         unsigned long sa_handler;
 236         struct sigaction * sa;
 237 
 238         while ((signr = current->signal & mask)) {
 239                 __asm__("bsf %2,%1\n\t"
 240                         "btrl %1,%0"
 241                         :"=m" (current->signal),"=r" (signr)
 242                         :"1" (signr));
 243                 sa = current->sigaction + signr;
 244                 signr++;
 245                 if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
 246                         current->exit_code = signr;
 247                         current->state = TASK_STOPPED;
 248                         notify_parent(current);
 249                         schedule();
 250                         if (!(signr = current->exit_code))
 251                                 continue;
 252                         current->exit_code = 0;
 253                         if (signr == SIGSTOP)
 254                                 continue;
 255                         if (_S(signr) & current->blocked) {
 256                                 current->signal |= _S(signr);
 257                                 continue;
 258                         }
 259                         sa = current->sigaction + signr - 1;
 260                 }
 261                 if (sa->sa_handler == SIG_IGN) {
 262                         if (signr != SIGCHLD)
 263                                 continue;
 264                         /* check for SIGCHLD: it's special */
 265                         while (sys_waitpid(-1,NULL,WNOHANG) > 0)
 266                                 /* nothing */;
 267                         continue;
 268                 }
 269                 if (sa->sa_handler == SIG_DFL) {
 270                         if (current->pid == 1)
 271                                 continue;
 272                         switch (signr) {
 273                         case SIGCONT: case SIGCHLD: case SIGWINCH:
 274                                 continue;
 275 
 276                         case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
 277                                 current->state = TASK_STOPPED;
 278                                 current->exit_code = signr;
 279                                 if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & 
 280                                                 SA_NOCLDSTOP))
 281                                         notify_parent(current);
 282                                 schedule();
 283                                 continue;
 284 
 285                         case SIGQUIT: case SIGILL: case SIGTRAP:
 286                         case SIGIOT: case SIGFPE: case SIGSEGV:
 287                                 if (core_dump(signr,regs))
 288                                         signr |= 0x80;
 289                                 /* fall through */
 290                         default:
 291                                 current->signal |= _S(signr & 0x7f);
 292                                 do_exit(signr);
 293                         }
 294                 }
 295                 /*
 296                  * OK, we're invoking a handler
 297                  */
 298                 if (regs->orig_eax >= 0) {
 299                         if (regs->eax == -ERESTARTNOHAND ||
 300                            (regs->eax == -ERESTARTSYS && !(sa->sa_flags & SA_RESTART)))
 301                                 regs->eax = -EINTR;
 302                 }
 303                 handler_signal |= 1 << (signr-1);
 304                 mask &= ~sa->sa_mask;
 305         }
 306         if (regs->orig_eax >= 0 &&
 307             (regs->eax == -ERESTARTNOHAND ||
 308              regs->eax == -ERESTARTSYS ||
 309              regs->eax == -ERESTARTNOINTR)) {
 310                 regs->eax = regs->orig_eax;
 311                 regs->eip -= 2;
 312         }
 313         if (!handler_signal)            /* no handler will be called - return 0 */
 314                 return 0;
 315         eip = regs->eip;
 316         frame = (unsigned long *) regs->esp;
 317         signr = 1;
 318         sa = current->sigaction;
 319         for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
 320                 if (mask > handler_signal)
 321                         break;
 322                 if (!(mask & handler_signal))
 323                         continue;
 324                 sa_handler = (unsigned long) sa->sa_handler;
 325                 if (sa->sa_flags & SA_ONESHOT)
 326                         sa->sa_handler = NULL;
 327 /* force a supervisor-mode page-in of the signal handler to reduce races */
 328                 __asm__("testb $0,%%fs:%0"::"m" (*(char *) sa_handler));
 329                 setup_frame(&frame,eip,regs,signr,sa_handler,oldmask);
 330                 eip = sa_handler;
 331                 current->blocked |= sa->sa_mask;
 332                 oldmask |= sa->sa_mask;
 333         }
 334         regs->esp = (unsigned long) frame;
 335         regs->eip = eip;                        /* "return" to the first handler */
 336         return 1;
 337 }

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