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 
  23 extern "C" int do_signal(unsigned long oldmask, struct pt_regs * regs);
  24 
  25 extern "C" int sys_sgetmask(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  26 {
  27         return current->blocked;
  28 }
  29 
  30 extern "C" int sys_ssetmask(int newmask)
     /* [previous][next][first][last][top][bottom][index][help] */
  31 {
  32         int old=current->blocked;
  33 
  34         current->blocked = newmask & _BLOCKABLE;
  35         return old;
  36 }
  37 
  38 extern "C" int sys_sigpending(sigset_t *set)
     /* [previous][next][first][last][top][bottom][index][help] */
  39 {
  40         int error;
  41         /* fill in "set" with signals pending but blocked. */
  42         error = verify_area(VERIFY_WRITE, set, 4);
  43         if (!error)
  44                 put_fs_long(current->blocked & current->signal, (unsigned long *)set);
  45         return error;
  46 }
  47 
  48 /*
  49  * atomically swap in the new signal mask, and wait for a signal.
  50  */
  51 extern "C" int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set)
     /* [previous][next][first][last][top][bottom][index][help] */
  52 {
  53         unsigned long mask;
  54         struct pt_regs * regs = (struct pt_regs *) &restart;
  55 
  56         mask = current->blocked;
  57         current->blocked = set & _BLOCKABLE;
  58         regs->eax = -EINTR;
  59         while (1) {
  60                 current->state = TASK_INTERRUPTIBLE;
  61                 schedule();
  62                 if (do_signal(mask,regs))
  63                         return -EINTR;
  64         }
  65 }
  66 
  67 /*
  68  * POSIX 3.3.1.3:
  69  *  "Setting a signal action to SIG_IGN for a signal that is pending
  70  *   shall cause the pending signal to be discarded, whether or not
  71  *   it is blocked" (but SIGCHLD is unspecified: linux leaves it alone).
  72  *
  73  *  "Setting a signal action to SIG_DFL for a signal that is pending
  74  *   and whose default action is to ignore the signal (for example,
  75  *   SIGCHLD), shall cause the pending signal to be discarded, whether
  76  *   or not it is blocked"
  77  *
  78  * Note the silly behaviour of SIGCHLD: SIG_IGN means that the signal
  79  * isn't actually ignored, but does automatic child reaping, while
  80  * SIG_DFL is explicitly said by POSIX to force the signal to be ignored..
  81  */
  82 static void check_pending(int signum)
     /* [previous][next][first][last][top][bottom][index][help] */
  83 {
  84         struct sigaction *p;
  85 
  86         p = signum - 1 + current->sigaction;
  87         if (p->sa_handler == SIG_IGN) {
  88                 if (signum == SIGCHLD)
  89                         return;
  90                 current->signal &= ~_S(signum);
  91                 return;
  92         }
  93         if (p->sa_handler == SIG_DFL) {
  94                 if (signum != SIGCONT && signum != SIGCHLD && signum != SIGWINCH)
  95                         return;
  96                 current->signal &= ~_S(signum);
  97                 return;
  98         }       
  99 }
 100 
 101 extern "C" int sys_signal(int signum, unsigned long handler)
     /* [previous][next][first][last][top][bottom][index][help] */
 102 {
 103         struct sigaction tmp;
 104 
 105         if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
 106                 return -EINVAL;
 107         if (handler >= TASK_SIZE)
 108                 return -EFAULT;
 109         tmp.sa_handler = (void (*)(int)) handler;
 110         tmp.sa_mask = 0;
 111         tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
 112         tmp.sa_restorer = NULL;
 113         handler = (long) current->sigaction[signum-1].sa_handler;
 114         current->sigaction[signum-1] = tmp;
 115         check_pending(signum);
 116         return handler;
 117 }
 118 
 119 extern "C" int sys_sigaction(int signum, const struct sigaction * action,
     /* [previous][next][first][last][top][bottom][index][help] */
 120         struct sigaction * oldaction)
 121 {
 122         struct sigaction new_sa, *p;
 123 
 124         if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
 125                 return -EINVAL;
 126         p = signum - 1 + current->sigaction;
 127         if (action) {
 128                 memcpy_fromfs(&new_sa, action, sizeof(struct sigaction));
 129                 if (new_sa.sa_flags & SA_NOMASK)
 130                         new_sa.sa_mask = 0;
 131                 else {
 132                         new_sa.sa_mask |= _S(signum);
 133                         new_sa.sa_mask &= _BLOCKABLE;
 134                 }
 135                 if (TASK_SIZE <= (unsigned long) new_sa.sa_handler)
 136                         return -EFAULT;
 137         }
 138         if (oldaction) {
 139                 if (!verify_area(VERIFY_WRITE,oldaction, sizeof(struct sigaction)))
 140                         memcpy_tofs(oldaction, p, sizeof(struct sigaction));
 141         }
 142         if (action) {
 143                 *p = new_sa;
 144                 check_pending(signum);
 145         }
 146         return 0;
 147 }
 148 
 149 extern "C" int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
 150 
 151 /*
 152  * This sets regs->esp even though we don't actually use sigstacks yet..
 153  */
 154 extern "C" int sys_sigreturn(unsigned long oldmask, unsigned long eip, unsigned long esp)
     /* [previous][next][first][last][top][bottom][index][help] */
 155 {
 156         struct pt_regs * regs;
 157 
 158         regs = (struct pt_regs *) &oldmask;
 159         current->blocked = oldmask & _BLOCKABLE;
 160         regs->eip = eip;
 161         regs->esp = esp;
 162         return 0;
 163 }
 164 
 165 /*
 166  * Set up a signal frame... Make the stack look the way iBCS2 expects
 167  * it to look.
 168  */
 169 static void setup_frame(unsigned long ** fp, unsigned long eip,
     /* [previous][next][first][last][top][bottom][index][help] */
 170         struct pt_regs * regs, int signr,
 171         unsigned long sa_handler, unsigned long oldmask)
 172 {
 173         unsigned long * frame;
 174 
 175 #define __CODE ((unsigned long)(frame+24))
 176 #define CODE(x) ((unsigned long *) ((x)+__CODE))
 177         frame = *fp - 32;
 178         verify_area(VERIFY_WRITE,frame,32*4);
 179 /* set up the "normal" stack seen by the signal handler (iBCS2) */
 180         put_fs_long(__CODE,frame);
 181         put_fs_long(signr, frame+1);
 182         put_fs_long(regs->gs, frame+2);
 183         put_fs_long(regs->fs, frame+3);
 184         put_fs_long(regs->es, frame+4);
 185         put_fs_long(regs->ds, frame+5);
 186         put_fs_long(regs->edi, frame+6);
 187         put_fs_long(regs->esi, frame+7);
 188         put_fs_long(regs->ebp, frame+8);
 189         put_fs_long(regs->esp, frame+9);
 190         put_fs_long(regs->ebx, frame+10);
 191         put_fs_long(regs->edx, frame+11);
 192         put_fs_long(regs->ecx, frame+12);
 193         put_fs_long(regs->eax, frame+13);
 194         put_fs_long(0, frame+14);               /* trapno */
 195         put_fs_long(0, frame+15);               /* err */
 196         put_fs_long(regs->eip, frame+16);
 197         put_fs_long(regs->cs, frame+17);
 198         put_fs_long(regs->eflags, frame+18);
 199         put_fs_long(regs->esp, frame+19);
 200         put_fs_long(regs->ss, frame+20);
 201         put_fs_long(0,frame+21);                /* 387 state pointer */
 202 /* linux extended stack - easier to handle.. */
 203         put_fs_long(regs->eflags, frame+22);
 204         put_fs_long(eip, frame+23);
 205 /* set up the return code... */
 206         put_fs_long(0x0000b858, CODE(0));       /* popl %eax ; movl $,%eax */
 207         put_fs_long(0x00bb0000, CODE(4));       /* movl $,%ebx */
 208         put_fs_long(0xcd000000, CODE(8));       /* int $0x80 */
 209         put_fs_long(0x0fa90f80, CODE(12));      /* pop %gs ; pop %fs */
 210         put_fs_long(0x611f07a1, CODE(16));      /* pop %es ; pop %ds ; popad */
 211         put_fs_long(0x20c48390, CODE(20));      /* nop ; addl $32,%esp */
 212         put_fs_long(0x0020c29d, CODE(24));      /* popfl ; ret $32 */
 213         put_fs_long(__NR_ssetmask, CODE(2));
 214         put_fs_long(oldmask, CODE(7));
 215         *fp = frame;
 216 #undef __CODE
 217 #undef CODE
 218 }
 219 
 220 /*
 221  * Note that 'init' is a special process: it doesn't get signals it doesn't
 222  * want to handle. Thus you cannot kill init even with a SIGKILL even by
 223  * mistake.
 224  *
 225  * Note that we go through the signals twice: once to check the signals that
 226  * the kernel can handle, and then we build all the user-level signal handling
 227  * stack-frames in one go after that.
 228  */
 229 extern "C" int do_signal(unsigned long oldmask, struct pt_regs * regs)
     /* [previous][next][first][last][top][bottom][index][help] */
 230 {
 231         unsigned long mask = ~current->blocked;
 232         unsigned long handler_signal = 0;
 233         unsigned long *frame = NULL;
 234         unsigned long eip = 0;
 235         unsigned long signr;
 236         unsigned long sa_handler;
 237         struct sigaction * sa;
 238 
 239         while ((signr = current->signal & mask)) {
 240                 __asm__("bsf %2,%1\n\t"
 241                         "btrl %1,%0"
 242                         :"=m" (current->signal),"=r" (signr)
 243                         :"1" (signr));
 244                 sa = current->sigaction + signr;
 245                 signr++;
 246                 if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
 247                         current->exit_code = signr;
 248                         current->state = TASK_STOPPED;
 249                         notify_parent(current);
 250                         schedule();
 251                         if (!(signr = current->exit_code))
 252                                 continue;
 253                         current->exit_code = 0;
 254                         if (signr == SIGSTOP)
 255                                 continue;
 256                         if (_S(signr) & current->blocked) {
 257                                 current->signal |= _S(signr);
 258                                 continue;
 259                         }
 260                         sa = current->sigaction + signr - 1;
 261                 }
 262                 if (sa->sa_handler == SIG_IGN) {
 263                         if (signr != SIGCHLD)
 264                                 continue;
 265                         /* check for SIGCHLD: it's special */
 266                         while (sys_waitpid(-1,NULL,WNOHANG) > 0)
 267                                 /* nothing */;
 268                         continue;
 269                 }
 270                 if (sa->sa_handler == SIG_DFL) {
 271                         if (current->pid == 1)
 272                                 continue;
 273                         switch (signr) {
 274                         case SIGCONT: case SIGCHLD: case SIGWINCH:
 275                                 continue;
 276 
 277                         case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
 278                                 current->state = TASK_STOPPED;
 279                                 current->exit_code = signr;
 280                                 if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & 
 281                                                 SA_NOCLDSTOP))
 282                                         notify_parent(current);
 283                                 schedule();
 284                                 continue;
 285 
 286                         case SIGQUIT: case SIGILL: case SIGTRAP:
 287                         case SIGIOT: case SIGFPE: case SIGSEGV:
 288                                 if (core_dump(signr,regs))
 289                                         signr |= 0x80;
 290                                 /* fall through */
 291                         default:
 292                                 current->signal |= _S(signr & 0x7f);
 293                                 do_exit(signr);
 294                         }
 295                 }
 296                 /*
 297                  * OK, we're invoking a handler
 298                  */
 299                 if (regs->orig_eax >= 0) {
 300                         if (regs->eax == -ERESTARTNOHAND ||
 301                            (regs->eax == -ERESTARTSYS && !(sa->sa_flags & SA_RESTART)))
 302                                 regs->eax = -EINTR;
 303                 }
 304                 handler_signal |= 1 << (signr-1);
 305                 mask &= ~sa->sa_mask;
 306         }
 307         if (regs->orig_eax >= 0 &&
 308             (regs->eax == -ERESTARTNOHAND ||
 309              regs->eax == -ERESTARTSYS ||
 310              regs->eax == -ERESTARTNOINTR)) {
 311                 regs->eax = regs->orig_eax;
 312                 regs->eip -= 2;
 313         }
 314         if (!handler_signal)            /* no handler will be called - return 0 */
 315                 return 0;
 316         eip = regs->eip;
 317         frame = (unsigned long *) regs->esp;
 318         signr = 1;
 319         sa = current->sigaction;
 320         if (regs->cs != USER_CS || regs->ss != USER_DS)
 321                 printk("Warning: signal handler with nonstandard code/stack segment\n");
 322         for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
 323                 if (mask > handler_signal)
 324                         break;
 325                 if (!(mask & handler_signal))
 326                         continue;
 327                 sa_handler = (unsigned long) sa->sa_handler;
 328                 if (sa->sa_flags & SA_ONESHOT)
 329                         sa->sa_handler = NULL;
 330 /* force a supervisor-mode page-in of the signal handler to reduce races */
 331                 __asm__("testb $0,%%fs:%0": :"m" (*(char *) sa_handler));
 332                 setup_frame(&frame,eip,regs,signr,sa_handler,oldmask);
 333                 eip = sa_handler;
 334                 current->blocked |= sa->sa_mask;
 335                 oldmask |= sa->sa_mask;
 336         }
 337         regs->esp = (unsigned long) frame;
 338         regs->eip = eip;                        /* "return" to the first handler */
 339         return 1;
 340 }

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