OSDN Git Service

crypto: talitos - HMAC SNOOP NO AFEU mode requires SW icv checking.
[android-x86/kernel.git] / kernel / signal.c
index 17428fe..2bb1f9d 100644 (file)
@@ -696,6 +696,48 @@ static inline bool si_fromuser(const struct siginfo *info)
                (!is_si_special(info) && SI_FROMUSER(info));
 }
 
+static int dequeue_synchronous_signal(siginfo_t *info)
+{
+       struct task_struct *tsk = current;
+       struct sigpending *pending = &tsk->pending;
+       struct sigqueue *q, *sync = NULL;
+
+       /*
+        * Might a synchronous signal be in the queue?
+        */
+       if (!((pending->signal.sig[0] & ~tsk->blocked.sig[0]) & SYNCHRONOUS_MASK))
+               return 0;
+
+       /*
+        * Return the first synchronous signal in the queue.
+        */
+       list_for_each_entry(q, &pending->list, list) {
+               /* Synchronous signals have a postive si_code */
+               if ((q->info.si_code > SI_USER) &&
+                   (sigmask(q->info.si_signo) & SYNCHRONOUS_MASK)) {
+                       sync = q;
+                       goto next;
+               }
+       }
+       return 0;
+next:
+       /*
+        * Check if there is another siginfo for the same signal.
+        */
+       list_for_each_entry_continue(q, &pending->list, list) {
+               if (q->info.si_signo == sync->info.si_signo)
+                       goto still_pending;
+       }
+
+       sigdelset(&pending->signal, sync->info.si_signo);
+       recalc_sigpending();
+still_pending:
+       list_del_init(&sync->list);
+       copy_siginfo(info, &sync->info);
+       __sigqueue_free(sync);
+       return info->si_signo;
+}
+
 /*
  * called with RCU read lock from check_kill_permission()
  */
@@ -991,7 +1033,7 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
 
        result = TRACE_SIGNAL_IGNORED;
        if (!prepare_signal(sig, t,
-                       from_ancestor_ns || (info == SEND_SIG_FORCED)))
+                       from_ancestor_ns || (info == SEND_SIG_PRIV) || (info == SEND_SIG_FORCED)))
                goto ret;
 
        pending = group ? &t->signal->shared_pending : &t->pending;
@@ -1392,6 +1434,10 @@ static int kill_something_info(int sig, struct siginfo *info, pid_t pid)
                return ret;
        }
 
+       /* -INT_MIN is undefined.  Exclude this case to avoid a UBSAN warning */
+       if (pid == INT_MIN)
+               return -ESRCH;
+
        read_lock(&tasklist_lock);
        if (pid != -1) {
                ret = __kill_pgrp_info(sig, info,
@@ -2194,6 +2240,16 @@ relock:
                goto relock;
        }
 
+       /* Has this task already been marked for death? */
+       if (signal_group_exit(signal)) {
+               ksig->info.si_signo = signr = SIGKILL;
+               sigdelset(&current->pending.signal, SIGKILL);
+               trace_signal_deliver(SIGKILL, SEND_SIG_NOINFO,
+                               &sighand->action[SIGKILL - 1]);
+               recalc_sigpending();
+               goto fatal;
+       }
+
        for (;;) {
                struct k_sigaction *ka;
 
@@ -2207,7 +2263,15 @@ relock:
                        goto relock;
                }
 
-               signr = dequeue_signal(current, &current->blocked, &ksig->info);
+               /*
+                * Signals generated by the execution of an instruction
+                * need to be delivered before any other pending signals
+                * so that the instruction pointer in the signal stack
+                * frame points to the faulting instruction.
+                */
+               signr = dequeue_synchronous_signal(&ksig->info);
+               if (!signr)
+                       signr = dequeue_signal(current, &current->blocked, &ksig->info);
 
                if (!signr)
                        break; /* will return 0 */
@@ -2289,6 +2353,7 @@ relock:
                        continue;
                }
 
+       fatal:
                spin_unlock_irq(&sighand->siglock);
 
                /*
@@ -3112,7 +3177,8 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
 }
 
 static int
-do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long sp)
+do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long sp,
+               size_t min_ss_size)
 {
        stack_t oss;
        int error;
@@ -3151,9 +3217,8 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s
                        ss_size = 0;
                        ss_sp = NULL;
                } else {
-                       error = -ENOMEM;
-                       if (ss_size < MINSIGSTKSZ)
-                               goto out;
+                       if (unlikely(ss_size < min_ss_size))
+                               return -ENOMEM;
                }
 
                current->sas_ss_sp = (unsigned long) ss_sp;
@@ -3176,12 +3241,14 @@ out:
 }
 SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss)
 {
-       return do_sigaltstack(uss, uoss, current_user_stack_pointer());
+       return do_sigaltstack(uss, uoss, current_user_stack_pointer(),
+                             MINSIGSTKSZ);
 }
 
 int restore_altstack(const stack_t __user *uss)
 {
-       int err = do_sigaltstack(uss, NULL, current_user_stack_pointer());
+       int err = do_sigaltstack(uss, NULL, current_user_stack_pointer(),
+                                       MINSIGSTKSZ);
        /* squash all but EFAULT for now */
        return err == -EFAULT ? err : 0;
 }
@@ -3222,7 +3289,8 @@ COMPAT_SYSCALL_DEFINE2(sigaltstack,
        set_fs(KERNEL_DS);
        ret = do_sigaltstack((stack_t __force __user *) (uss_ptr ? &uss : NULL),
                             (stack_t __force __user *) &uoss,
-                            compat_user_stack_pointer());
+                            compat_user_stack_pointer(),
+                            COMPAT_MINSIGSTKSZ);
        set_fs(seg);
        if (ret >= 0 && uoss_ptr)  {
                if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(compat_stack_t)) ||