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

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