Skip to content

Commit

Permalink
arm64: signal: split frame link record from sigcontext structure
Browse files Browse the repository at this point in the history
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: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
  • Loading branch information
Dave Martin authored and Will Deacon committed Jun 20, 2017
1 parent 8f36094 commit 20987de
Showing 1 changed file with 32 additions and 18 deletions.
50 changes: 32 additions & 18 deletions arch/arm64/kernel/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include <linux/compat.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/personality.h>
#include <linux/freezer.h>
Expand All @@ -41,10 +42,18 @@
struct rt_sigframe {
struct siginfo info;
struct ucontext uc;
};

struct frame_record {
u64 fp;
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)
{
struct fpsimd_state *fpsimd = &current->thread.fpsimd_state;
Expand Down Expand Up @@ -162,16 +171,17 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
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)
{
int i, err = 0;
struct rt_sigframe __user *sf = user->sigframe;
void *aux = sf->uc.uc_mcontext.__reserved;
struct _aarch64_ctx *end;

/* set up the stack frame for unwinding */
__put_user_error(regs->regs[29], &sf->fp, err);
__put_user_error(regs->regs[30], &sf->lr, err);
__put_user_error(regs->regs[29], &user->next_frame->fp, err);
__put_user_error(regs->regs[30], &user->next_frame->lr, err);

for (i = 0; i < 31; i++)
__put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i],
Expand Down Expand Up @@ -209,34 +219,36 @@ static int setup_sigframe(struct rt_sigframe __user *sf,
return err;
}

static struct rt_sigframe __user *get_sigframe(struct ksignal *ksig,
struct pt_regs *regs)
static int get_sigframe(struct rt_sigframe_user_layout *user,
struct ksignal *ksig, struct pt_regs *regs)
{
unsigned long sp, sp_top;
struct rt_sigframe __user *frame;

sp = sp_top = sigsp(regs->sp, ksig);

sp = (sp - sizeof(struct rt_sigframe)) & ~15;
frame = (struct rt_sigframe __user *)sp;
sp = round_down(sp - sizeof(struct frame_record), 16);
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.
*/
if (!access_ok(VERIFY_WRITE, frame, sp_top - sp))
frame = NULL;
if (!access_ok(VERIFY_WRITE, user->sigframe, sp_top - sp))
return -EFAULT;

return frame;
return 0;
}

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;

regs->regs[0] = usig;
regs->sp = (unsigned long)frame;
regs->regs[29] = regs->sp + offsetof(struct rt_sigframe, fp);
regs->sp = (unsigned long)user->sigframe;
regs->regs[29] = (unsigned long)&user->next_frame->fp;
regs->pc = (unsigned long)ka->sa.sa_handler;

if (ka->sa.sa_flags & SA_RESTORER)
Expand All @@ -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,
struct pt_regs *regs)
{
struct rt_sigframe_user_layout user;
struct rt_sigframe __user *frame;
int err = 0;

frame = get_sigframe(ksig, regs);
if (!frame)
if (get_sigframe(&user, ksig, regs))
return 1;

frame = user.sigframe;

__put_user_error(0, &frame->uc.uc_flags, err);
__put_user_error(NULL, &frame->uc.uc_link, err);

err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
err |= setup_sigframe(frame, regs, set);
err |= setup_sigframe(&user, regs, set);
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) {
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
regs->regs[1] = (unsigned long)&frame->info;
Expand Down

0 comments on commit 20987de

Please sign in to comment.