signal/ia64: Use the generic force_sigsegv in setup_frame
authorEric W. Biederman <ebiederm@xmission.com>
Mon, 16 Apr 2018 18:55:06 +0000 (13:55 -0500)
committerEric W. Biederman <ebiederm@xmission.com>
Thu, 27 Sep 2018 19:57:52 +0000 (21:57 +0200)
The ia64 handling of failure to setup a signal frame has been trying
to set overlapping fields in struct siginfo since 2.3.43.  The si_pid
and si_uid fields are stomped when the si_addr field is set.  The
si_code of SI_KERNEL indicates that si_pid and si_uid should be valid,
and that si_addr does not exist.

Being at odds with the definition of SI_KERNEL and with nothing to
indicate that this was a signal frame setup failure there is no way
for userspace to know that si_addr was filled out instead.

In practice failure to setup a signal frame is rare, and si_pid and
si_uid are always set to 0 when si_code is SI_KERNEL so I expect no
one has looked closely enough before to see this weirdness.  Further
the only difference between force_sigsegv_info and the generic
force_sigsegv other than the return code is that force_sigsegv_info
stomps the si_uid and si_pid fields.

Remove the bug and simplify the code by using force_sigsegv in this
case just like other architectures.

Fixes: 2.3.43
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: linux-ia64@vger.kernel.org
Acked-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
arch/ia64/kernel/signal.c

index d1234a5..01fc133 100644 (file)
@@ -232,37 +232,6 @@ rbs_on_sig_stack (unsigned long bsp)
 }
 
 static long
-force_sigsegv_info (int sig, void __user *addr)
-{
-       unsigned long flags;
-       struct siginfo si;
-
-       clear_siginfo(&si);
-       if (sig == SIGSEGV) {
-               /*
-                * Acquiring siglock around the sa_handler-update is almost
-                * certainly overkill, but this isn't a
-                * performance-critical path and I'd rather play it safe
-                * here than having to debug a nasty race if and when
-                * something changes in kernel/signal.c that would make it
-                * no longer safe to modify sa_handler without holding the
-                * lock.
-                */
-               spin_lock_irqsave(&current->sighand->siglock, flags);
-               current->sighand->action[sig - 1].sa.sa_handler = SIG_DFL;
-               spin_unlock_irqrestore(&current->sighand->siglock, flags);
-       }
-       si.si_signo = SIGSEGV;
-       si.si_errno = 0;
-       si.si_code = SI_KERNEL;
-       si.si_pid = task_pid_vnr(current);
-       si.si_uid = from_kuid_munged(current_user_ns(), current_uid());
-       si.si_addr = addr;
-       force_sig_info(SIGSEGV, &si, current);
-       return 1;
-}
-
-static long
 setup_frame(struct ksignal *ksig, sigset_t *set, struct sigscratch *scr)
 {
        extern char __kernel_sigtramp[];
@@ -295,15 +264,18 @@ setup_frame(struct ksignal *ksig, sigset_t *set, struct sigscratch *scr)
                         * instead so we will die with SIGSEGV.
                         */
                        check_sp = (new_sp - sizeof(*frame)) & -STACK_ALIGN;
-                       if (!likely(on_sig_stack(check_sp)))
-                               return force_sigsegv_info(ksig->sig, (void __user *)
-                                                         check_sp);
+                       if (!likely(on_sig_stack(check_sp))) {
+                               force_sigsegv(ksig->sig, current);
+                               return 1;
+                       }
                }
        }
        frame = (void __user *) ((new_sp - sizeof(*frame)) & -STACK_ALIGN);
 
-       if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
-               return force_sigsegv_info(ksig->sig, frame);
+       if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) {
+               force_sigsegv(ksig->sig, current);
+               return 1;
+       }
 
        err  = __put_user(ksig->sig, &frame->arg0);
        err |= __put_user(&frame->info, &frame->arg1);
@@ -317,8 +289,10 @@ setup_frame(struct ksignal *ksig, sigset_t *set, struct sigscratch *scr)
        err |= __save_altstack(&frame->sc.sc_stack, scr->pt.r12);
        err |= setup_sigcontext(&frame->sc, set, scr);
 
-       if (unlikely(err))
-               return force_sigsegv_info(ksig->sig, frame);
+       if (unlikely(err)) {
+               force_sigsegv(ksig->sig, current);
+               return 1;
+       }
 
        scr->pt.r12 = (unsigned long) frame - 16;       /* new stack pointer */
        scr->pt.ar_fpsr = FPSR_DEFAULT;                 /* reset fpsr for signal handler */