|
@@ -27,6 +27,7 @@
|
|
|
#include <asm/uaccess.h>
|
|
|
#include <asm/i387.h>
|
|
|
#include <asm/vdso.h>
|
|
|
+#include <asm/syscall.h>
|
|
|
#include <asm/syscalls.h>
|
|
|
|
|
|
#include "sigframe.h"
|
|
@@ -112,6 +113,27 @@ asmlinkage int sys_sigaltstack(unsigned long bx)
|
|
|
return do_sigaltstack(uss, uoss, regs->sp);
|
|
|
}
|
|
|
|
|
|
+#define COPY(x) { \
|
|
|
+ err |= __get_user(regs->x, &sc->x); \
|
|
|
+}
|
|
|
+
|
|
|
+#define COPY_SEG(seg) { \
|
|
|
+ unsigned short tmp; \
|
|
|
+ err |= __get_user(tmp, &sc->seg); \
|
|
|
+ regs->seg = tmp; \
|
|
|
+}
|
|
|
+
|
|
|
+#define COPY_SEG_STRICT(seg) { \
|
|
|
+ unsigned short tmp; \
|
|
|
+ err |= __get_user(tmp, &sc->seg); \
|
|
|
+ regs->seg = tmp | 3; \
|
|
|
+}
|
|
|
+
|
|
|
+#define GET_SEG(seg) { \
|
|
|
+ unsigned short tmp; \
|
|
|
+ err |= __get_user(tmp, &sc->seg); \
|
|
|
+ loadsegment(seg, tmp); \
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
* Do a signal return; undo the signal stack.
|
|
@@ -120,28 +142,13 @@ static int
|
|
|
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
|
|
|
unsigned long *pax)
|
|
|
{
|
|
|
+ void __user *buf;
|
|
|
+ unsigned int tmpflags;
|
|
|
unsigned int err = 0;
|
|
|
|
|
|
/* Always make any pending restarted system calls return -EINTR */
|
|
|
current_thread_info()->restart_block.fn = do_no_restart_syscall;
|
|
|
|
|
|
-#define COPY(x) err |= __get_user(regs->x, &sc->x)
|
|
|
-
|
|
|
-#define COPY_SEG(seg) \
|
|
|
- { unsigned short tmp; \
|
|
|
- err |= __get_user(tmp, &sc->seg); \
|
|
|
- regs->seg = tmp; }
|
|
|
-
|
|
|
-#define COPY_SEG_STRICT(seg) \
|
|
|
- { unsigned short tmp; \
|
|
|
- err |= __get_user(tmp, &sc->seg); \
|
|
|
- regs->seg = tmp|3; }
|
|
|
-
|
|
|
-#define GET_SEG(seg) \
|
|
|
- { unsigned short tmp; \
|
|
|
- err |= __get_user(tmp, &sc->seg); \
|
|
|
- loadsegment(seg, tmp); }
|
|
|
-
|
|
|
GET_SEG(gs);
|
|
|
COPY_SEG(fs);
|
|
|
COPY_SEG(es);
|
|
@@ -151,21 +158,12 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
|
|
|
COPY_SEG_STRICT(cs);
|
|
|
COPY_SEG_STRICT(ss);
|
|
|
|
|
|
- {
|
|
|
- unsigned int tmpflags;
|
|
|
-
|
|
|
- err |= __get_user(tmpflags, &sc->flags);
|
|
|
- regs->flags = (regs->flags & ~FIX_EFLAGS) |
|
|
|
- (tmpflags & FIX_EFLAGS);
|
|
|
- regs->orig_ax = -1; /* disable syscall checks */
|
|
|
- }
|
|
|
-
|
|
|
- {
|
|
|
- void __user *buf;
|
|
|
+ err |= __get_user(tmpflags, &sc->flags);
|
|
|
+ regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
|
|
|
+ regs->orig_ax = -1; /* disable syscall checks */
|
|
|
|
|
|
- err |= __get_user(buf, &sc->fpstate);
|
|
|
- err |= restore_i387_xstate(buf);
|
|
|
- }
|
|
|
+ err |= __get_user(buf, &sc->fpstate);
|
|
|
+ err |= restore_i387_xstate(buf);
|
|
|
|
|
|
err |= __get_user(*pax, &sc->ax);
|
|
|
return err;
|
|
@@ -214,9 +212,8 @@ badframe:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-asmlinkage int sys_rt_sigreturn(unsigned long __unused)
|
|
|
+static long do_rt_sigreturn(struct pt_regs *regs)
|
|
|
{
|
|
|
- struct pt_regs *regs = (struct pt_regs *)&__unused;
|
|
|
struct rt_sigframe __user *frame;
|
|
|
unsigned long ax;
|
|
|
sigset_t set;
|
|
@@ -242,10 +239,17 @@ asmlinkage int sys_rt_sigreturn(unsigned long __unused)
|
|
|
return ax;
|
|
|
|
|
|
badframe:
|
|
|
- force_sig(SIGSEGV, current);
|
|
|
+ signal_fault(regs, frame, "rt_sigreturn");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+asmlinkage int sys_rt_sigreturn(unsigned long __unused)
|
|
|
+{
|
|
|
+ struct pt_regs *regs = (struct pt_regs *)&__unused;
|
|
|
+
|
|
|
+ return do_rt_sigreturn(regs);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Set up a signal frame.
|
|
|
*/
|
|
@@ -337,39 +341,29 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
|
|
|
- struct pt_regs *regs)
|
|
|
+__setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
|
|
|
+ struct pt_regs *regs)
|
|
|
{
|
|
|
struct sigframe __user *frame;
|
|
|
void __user *restorer;
|
|
|
int err = 0;
|
|
|
- int usig;
|
|
|
void __user *fpstate = NULL;
|
|
|
|
|
|
frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
|
|
|
|
|
|
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
|
|
|
- goto give_sigsegv;
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
- usig = current_thread_info()->exec_domain
|
|
|
- && current_thread_info()->exec_domain->signal_invmap
|
|
|
- && sig < 32
|
|
|
- ? current_thread_info()->exec_domain->signal_invmap[sig]
|
|
|
- : sig;
|
|
|
+ if (__put_user(sig, &frame->sig))
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
- err = __put_user(usig, &frame->sig);
|
|
|
- if (err)
|
|
|
- goto give_sigsegv;
|
|
|
-
|
|
|
- err = setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]);
|
|
|
- if (err)
|
|
|
- goto give_sigsegv;
|
|
|
+ if (setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
if (_NSIG_WORDS > 1) {
|
|
|
- err = __copy_to_user(&frame->extramask, &set->sig[1],
|
|
|
- sizeof(frame->extramask));
|
|
|
- if (err)
|
|
|
- goto give_sigsegv;
|
|
|
+ if (__copy_to_user(&frame->extramask, &set->sig[1],
|
|
|
+ sizeof(frame->extramask)))
|
|
|
+ return -EFAULT;
|
|
|
}
|
|
|
|
|
|
if (current->mm->context.vdso)
|
|
@@ -394,7 +388,7 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
|
|
|
err |= __put_user(0x80cd, (short __user *)(frame->retcode+6));
|
|
|
|
|
|
if (err)
|
|
|
- goto give_sigsegv;
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
/* Set up registers for signal handler */
|
|
|
regs->sp = (unsigned long)frame;
|
|
@@ -409,38 +403,27 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
|
|
|
regs->cs = __USER_CS;
|
|
|
|
|
|
return 0;
|
|
|
-
|
|
|
-give_sigsegv:
|
|
|
- force_sigsegv(sig, current);
|
|
|
- return -EFAULT;
|
|
|
}
|
|
|
|
|
|
-static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|
|
- sigset_t *set, struct pt_regs *regs)
|
|
|
+static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|
|
+ sigset_t *set, struct pt_regs *regs)
|
|
|
{
|
|
|
struct rt_sigframe __user *frame;
|
|
|
void __user *restorer;
|
|
|
int err = 0;
|
|
|
- int usig;
|
|
|
void __user *fpstate = NULL;
|
|
|
|
|
|
frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
|
|
|
|
|
|
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
|
|
|
- goto give_sigsegv;
|
|
|
-
|
|
|
- usig = current_thread_info()->exec_domain
|
|
|
- && current_thread_info()->exec_domain->signal_invmap
|
|
|
- && sig < 32
|
|
|
- ? current_thread_info()->exec_domain->signal_invmap[sig]
|
|
|
- : sig;
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
- err |= __put_user(usig, &frame->sig);
|
|
|
+ err |= __put_user(sig, &frame->sig);
|
|
|
err |= __put_user(&frame->info, &frame->pinfo);
|
|
|
err |= __put_user(&frame->uc, &frame->puc);
|
|
|
err |= copy_siginfo_to_user(&frame->info, info);
|
|
|
if (err)
|
|
|
- goto give_sigsegv;
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
/* Create the ucontext. */
|
|
|
if (cpu_has_xsave)
|
|
@@ -456,7 +439,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|
|
regs, set->sig[0]);
|
|
|
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
|
|
|
if (err)
|
|
|
- goto give_sigsegv;
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
/* Set up to return from userspace. */
|
|
|
restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn);
|
|
@@ -476,12 +459,12 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|
|
err |= __put_user(0x80cd, (short __user *)(frame->retcode+5));
|
|
|
|
|
|
if (err)
|
|
|
- goto give_sigsegv;
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
/* Set up registers for signal handler */
|
|
|
regs->sp = (unsigned long)frame;
|
|
|
regs->ip = (unsigned long)ka->sa.sa_handler;
|
|
|
- regs->ax = (unsigned long)usig;
|
|
|
+ regs->ax = (unsigned long)sig;
|
|
|
regs->dx = (unsigned long)&frame->info;
|
|
|
regs->cx = (unsigned long)&frame->uc;
|
|
|
|
|
@@ -491,15 +474,48 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|
|
regs->cs = __USER_CS;
|
|
|
|
|
|
return 0;
|
|
|
-
|
|
|
-give_sigsegv:
|
|
|
- force_sigsegv(sig, current);
|
|
|
- return -EFAULT;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* OK, we're invoking a handler:
|
|
|
*/
|
|
|
+static int signr_convert(int sig)
|
|
|
+{
|
|
|
+ struct thread_info *info = current_thread_info();
|
|
|
+
|
|
|
+ if (info->exec_domain && info->exec_domain->signal_invmap && sig < 32)
|
|
|
+ return info->exec_domain->signal_invmap[sig];
|
|
|
+ return sig;
|
|
|
+}
|
|
|
+
|
|
|
+#define is_ia32 1
|
|
|
+#define ia32_setup_frame __setup_frame
|
|
|
+#define ia32_setup_rt_frame __setup_rt_frame
|
|
|
+
|
|
|
+static int
|
|
|
+setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|
|
+ sigset_t *set, struct pt_regs *regs)
|
|
|
+{
|
|
|
+ int usig = signr_convert(sig);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Set up the stack frame */
|
|
|
+ if (is_ia32) {
|
|
|
+ if (ka->sa.sa_flags & SA_SIGINFO)
|
|
|
+ ret = ia32_setup_rt_frame(usig, ka, info, set, regs);
|
|
|
+ else
|
|
|
+ ret = ia32_setup_frame(usig, ka, set, regs);
|
|
|
+ } else
|
|
|
+ ret = __setup_rt_frame(sig, ka, info, set, regs);
|
|
|
+
|
|
|
+ if (ret) {
|
|
|
+ force_sigsegv(sig, current);
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
|
|
|
sigset_t *oldset, struct pt_regs *regs)
|
|
@@ -507,9 +523,9 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
|
|
|
int ret;
|
|
|
|
|
|
/* Are we from a system call? */
|
|
|
- if ((long)regs->orig_ax >= 0) {
|
|
|
+ if (syscall_get_nr(current, regs) >= 0) {
|
|
|
/* If so, check system call restarting.. */
|
|
|
- switch (regs->ax) {
|
|
|
+ switch (syscall_get_error(current, regs)) {
|
|
|
case -ERESTART_RESTARTBLOCK:
|
|
|
case -ERESTARTNOHAND:
|
|
|
regs->ax = -EINTR;
|
|
@@ -536,15 +552,20 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
|
|
|
likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
|
|
|
regs->flags &= ~X86_EFLAGS_TF;
|
|
|
|
|
|
- /* Set up the stack frame */
|
|
|
- if (ka->sa.sa_flags & SA_SIGINFO)
|
|
|
- ret = setup_rt_frame(sig, ka, info, oldset, regs);
|
|
|
- else
|
|
|
- ret = setup_frame(sig, ka, oldset, regs);
|
|
|
+ ret = setup_rt_frame(sig, ka, info, oldset, regs);
|
|
|
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
+#ifdef CONFIG_X86_64
|
|
|
+ /*
|
|
|
+ * This has nothing to do with segment registers,
|
|
|
+ * despite the name. This magic affects uaccess.h
|
|
|
+ * macros' behavior. Reset it to the normal setting.
|
|
|
+ */
|
|
|
+ set_fs(USER_DS);
|
|
|
+#endif
|
|
|
+
|
|
|
/*
|
|
|
* Clear the direction flag as per the ABI for function entry.
|
|
|
*/
|
|
@@ -571,6 +592,7 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#define NR_restart_syscall __NR_restart_syscall
|
|
|
/*
|
|
|
* Note that 'init' is a special process: it doesn't get signals it doesn't
|
|
|
* want to handle. Thus you cannot kill init even with a SIGKILL even by
|
|
@@ -623,9 +645,9 @@ static void do_signal(struct pt_regs *regs)
|
|
|
}
|
|
|
|
|
|
/* Did we come from a system call? */
|
|
|
- if ((long)regs->orig_ax >= 0) {
|
|
|
+ if (syscall_get_nr(current, regs) >= 0) {
|
|
|
/* Restart the system call - no handlers present */
|
|
|
- switch (regs->ax) {
|
|
|
+ switch (syscall_get_error(current, regs)) {
|
|
|
case -ERESTARTNOHAND:
|
|
|
case -ERESTARTSYS:
|
|
|
case -ERESTARTNOINTR:
|
|
@@ -634,7 +656,7 @@ static void do_signal(struct pt_regs *regs)
|
|
|
break;
|
|
|
|
|
|
case -ERESTART_RESTARTBLOCK:
|
|
|
- regs->ax = __NR_restart_syscall;
|
|
|
+ regs->ax = NR_restart_syscall;
|
|
|
regs->ip -= 2;
|
|
|
break;
|
|
|
}
|
|
@@ -657,6 +679,12 @@ static void do_signal(struct pt_regs *regs)
|
|
|
void
|
|
|
do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
|
|
|
{
|
|
|
+#if defined(CONFIG_X86_64) && defined(CONFIG_X86_MCE)
|
|
|
+ /* notify userspace of pending MCEs */
|
|
|
+ if (thread_info_flags & _TIF_MCE_NOTIFY)
|
|
|
+ mce_notify_user();
|
|
|
+#endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
|
|
|
+
|
|
|
/* deal with pending signal delivery */
|
|
|
if (thread_info_flags & _TIF_SIGPENDING)
|
|
|
do_signal(regs);
|
|
@@ -666,5 +694,23 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
|
|
|
tracehook_notify_resume(regs);
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_X86_32
|
|
|
clear_thread_flag(TIF_IRET);
|
|
|
+#endif /* CONFIG_X86_32 */
|
|
|
+}
|
|
|
+
|
|
|
+void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
|
|
|
+{
|
|
|
+ struct task_struct *me = current;
|
|
|
+
|
|
|
+ if (show_unhandled_signals && printk_ratelimit()) {
|
|
|
+ printk(KERN_INFO
|
|
|
+ "%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx",
|
|
|
+ me->comm, me->pid, where, frame,
|
|
|
+ regs->ip, regs->sp, regs->orig_ax);
|
|
|
+ print_vma_addr(" in ", regs->ip);
|
|
|
+ printk(KERN_CONT "\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ force_sig(SIGSEGV, me);
|
|
|
}
|