Commit 20987de3 authored by Dave Martin's avatar Dave Martin Committed by Will Deacon

arm64: signal: split frame link record from sigcontext structure

In order to be able to increase the amount of the data currently
written to the __reserved[] array in the signal frame, it is
necessary to overwrite the locations currently occupied by the
{fp,lr} frame link record pushed at the top of the signal stack.

In order for this to work, this patch detaches the frame link
record from struct rt_sigframe and places it separately at the top
of the signal stack.  This will allow subsequent patches to insert
data between it and __reserved[].

This change relies on the non-ABI status of the placement of the
frame record with respect to struct sigframe: this status is
undocumented, but the placement is not declared or described in the
user headers, and known unwinder implementations (libgcc,
libunwind, gdb) appear not to rely on it.
Reviewed-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Signed-off-by: default avatarDave Martin <Dave.Martin@arm.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent 8f360948
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/signal.h> #include <linux/signal.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/freezer.h> #include <linux/freezer.h>
...@@ -41,10 +42,18 @@ ...@@ -41,10 +42,18 @@
struct rt_sigframe { struct rt_sigframe {
struct siginfo info; struct siginfo info;
struct ucontext uc; struct ucontext uc;
};
struct frame_record {
u64 fp; u64 fp;
u64 lr; u64 lr;
}; };
struct rt_sigframe_user_layout {
struct rt_sigframe __user *sigframe;
struct frame_record __user *next_frame;
};
static int preserve_fpsimd_context(struct fpsimd_context __user *ctx) static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
{ {
struct fpsimd_state *fpsimd = &current->thread.fpsimd_state; struct fpsimd_state *fpsimd = &current->thread.fpsimd_state;
...@@ -162,16 +171,17 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) ...@@ -162,16 +171,17 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
return 0; return 0;
} }
static int setup_sigframe(struct rt_sigframe __user *sf, static int setup_sigframe(struct rt_sigframe_user_layout *user,
struct pt_regs *regs, sigset_t *set) struct pt_regs *regs, sigset_t *set)
{ {
int i, err = 0; int i, err = 0;
struct rt_sigframe __user *sf = user->sigframe;
void *aux = sf->uc.uc_mcontext.__reserved; void *aux = sf->uc.uc_mcontext.__reserved;
struct _aarch64_ctx *end; struct _aarch64_ctx *end;
/* set up the stack frame for unwinding */ /* set up the stack frame for unwinding */
__put_user_error(regs->regs[29], &sf->fp, err); __put_user_error(regs->regs[29], &user->next_frame->fp, err);
__put_user_error(regs->regs[30], &sf->lr, err); __put_user_error(regs->regs[30], &user->next_frame->lr, err);
for (i = 0; i < 31; i++) for (i = 0; i < 31; i++)
__put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], __put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i],
...@@ -209,34 +219,36 @@ static int setup_sigframe(struct rt_sigframe __user *sf, ...@@ -209,34 +219,36 @@ static int setup_sigframe(struct rt_sigframe __user *sf,
return err; return err;
} }
static struct rt_sigframe __user *get_sigframe(struct ksignal *ksig, static int get_sigframe(struct rt_sigframe_user_layout *user,
struct pt_regs *regs) struct ksignal *ksig, struct pt_regs *regs)
{ {
unsigned long sp, sp_top; unsigned long sp, sp_top;
struct rt_sigframe __user *frame;
sp = sp_top = sigsp(regs->sp, ksig); sp = sp_top = sigsp(regs->sp, ksig);
sp = (sp - sizeof(struct rt_sigframe)) & ~15; sp = round_down(sp - sizeof(struct frame_record), 16);
frame = (struct rt_sigframe __user *)sp; user->next_frame = (struct frame_record __user *)sp;
sp = round_down(sp - sizeof(struct rt_sigframe), 16);
user->sigframe = (struct rt_sigframe __user *)sp;
/* /*
* Check that we can actually write to the signal frame. * Check that we can actually write to the signal frame.
*/ */
if (!access_ok(VERIFY_WRITE, frame, sp_top - sp)) if (!access_ok(VERIFY_WRITE, user->sigframe, sp_top - sp))
frame = NULL; return -EFAULT;
return frame; return 0;
} }
static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
void __user *frame, int usig) struct rt_sigframe_user_layout *user, int usig)
{ {
__sigrestore_t sigtramp; __sigrestore_t sigtramp;
regs->regs[0] = usig; regs->regs[0] = usig;
regs->sp = (unsigned long)frame; regs->sp = (unsigned long)user->sigframe;
regs->regs[29] = regs->sp + offsetof(struct rt_sigframe, fp); regs->regs[29] = (unsigned long)&user->next_frame->fp;
regs->pc = (unsigned long)ka->sa.sa_handler; regs->pc = (unsigned long)ka->sa.sa_handler;
if (ka->sa.sa_flags & SA_RESTORER) if (ka->sa.sa_flags & SA_RESTORER)
...@@ -250,20 +262,22 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, ...@@ -250,20 +262,22 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
struct pt_regs *regs) struct pt_regs *regs)
{ {
struct rt_sigframe_user_layout user;
struct rt_sigframe __user *frame; struct rt_sigframe __user *frame;
int err = 0; int err = 0;
frame = get_sigframe(ksig, regs); if (get_sigframe(&user, ksig, regs))
if (!frame)
return 1; return 1;
frame = user.sigframe;
__put_user_error(0, &frame->uc.uc_flags, err); __put_user_error(0, &frame->uc.uc_flags, err);
__put_user_error(NULL, &frame->uc.uc_link, err); __put_user_error(NULL, &frame->uc.uc_link, err);
err |= __save_altstack(&frame->uc.uc_stack, regs->sp); err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
err |= setup_sigframe(frame, regs, set); err |= setup_sigframe(&user, regs, set);
if (err == 0) { if (err == 0) {
setup_return(regs, &ksig->ka, frame, usig); setup_return(regs, &ksig->ka, &user, usig);
if (ksig->ka.sa.sa_flags & SA_SIGINFO) { if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
err |= copy_siginfo_to_user(&frame->info, &ksig->info); err |= copy_siginfo_to_user(&frame->info, &ksig->info);
regs->regs[1] = (unsigned long)&frame->info; regs->regs[1] = (unsigned long)&frame->info;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment