Skip to content

Commit

Permalink
sparc: Harden signal return frame checks.
Browse files Browse the repository at this point in the history
[ Upstream commit d11c2a0 ]

All signal frames must be at least 16-byte aligned, because that is
the alignment we explicitly create when we build signal return stack
frames.

All stack pointers must be at least 8-byte aligned.

Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
davem330 authored and gregkh committed Jun 24, 2016
1 parent 6bb3290 commit 1fda90c
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 45 deletions.
46 changes: 30 additions & 16 deletions arch/sparc/kernel/signal32.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,24 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
return 0;
}

/* Checks if the fp is valid. We always build signal frames which are
* 16-byte aligned, therefore we can always enforce that the restore
* frame has that property as well.
*/
static bool invalid_frame_pointer(void __user *fp, int fplen)
{
if ((((unsigned long) fp) & 15) ||
((unsigned long)fp) > 0x100000000ULL - fplen)
return true;
return false;
}

void do_sigreturn32(struct pt_regs *regs)
{
struct signal_frame32 __user *sf;
compat_uptr_t fpu_save;
compat_uptr_t rwin_save;
unsigned int psr;
unsigned int psr, ufp;
unsigned pc, npc;
sigset_t set;
compat_sigset_t seta;
Expand All @@ -158,11 +170,16 @@ void do_sigreturn32(struct pt_regs *regs)
sf = (struct signal_frame32 __user *) regs->u_regs[UREG_FP];

/* 1. Make sure we are not getting garbage from the user */
if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) ||
(((unsigned long) sf) & 3))
if (invalid_frame_pointer(sf, sizeof(*sf)))
goto segv;

if (get_user(ufp, &sf->info.si_regs.u_regs[UREG_FP]))
goto segv;

if (ufp & 0x7)
goto segv;

if (get_user(pc, &sf->info.si_regs.pc) ||
if (__get_user(pc, &sf->info.si_regs.pc) ||
__get_user(npc, &sf->info.si_regs.npc))
goto segv;

Expand Down Expand Up @@ -227,7 +244,7 @@ void do_sigreturn32(struct pt_regs *regs)
asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
{
struct rt_signal_frame32 __user *sf;
unsigned int psr, pc, npc;
unsigned int psr, pc, npc, ufp;
compat_uptr_t fpu_save;
compat_uptr_t rwin_save;
sigset_t set;
Expand All @@ -242,11 +259,16 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
sf = (struct rt_signal_frame32 __user *) regs->u_regs[UREG_FP];

/* 1. Make sure we are not getting garbage from the user */
if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) ||
(((unsigned long) sf) & 3))
if (invalid_frame_pointer(sf, sizeof(*sf)))
goto segv;

if (get_user(pc, &sf->regs.pc) ||
if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
goto segv;

if (ufp & 0x7)
goto segv;

if (__get_user(pc, &sf->regs.pc) ||
__get_user(npc, &sf->regs.npc))
goto segv;

Expand Down Expand Up @@ -307,14 +329,6 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
force_sig(SIGSEGV, current);
}

/* Checks if the fp is valid */
static int invalid_frame_pointer(void __user *fp, int fplen)
{
if ((((unsigned long) fp) & 7) || ((unsigned long)fp) > 0x100000000ULL - fplen)
return 1;
return 0;
}

static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize)
{
unsigned long sp;
Expand Down
41 changes: 26 additions & 15 deletions arch/sparc/kernel/signal_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,22 @@ struct rt_signal_frame {
#define SF_ALIGNEDSZ (((sizeof(struct signal_frame) + 7) & (~7)))
#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7)))

/* Checks if the fp is valid. We always build signal frames which are
* 16-byte aligned, therefore we can always enforce that the restore
* frame has that property as well.
*/
static inline bool invalid_frame_pointer(void __user *fp, int fplen)
{
if ((((unsigned long) fp) & 15) || !__access_ok((unsigned long)fp, fplen))
return true;

return false;
}

asmlinkage void do_sigreturn(struct pt_regs *regs)
{
unsigned long up_psr, pc, npc, ufp;
struct signal_frame __user *sf;
unsigned long up_psr, pc, npc;
sigset_t set;
__siginfo_fpu_t __user *fpu_save;
__siginfo_rwin_t __user *rwin_save;
Expand All @@ -77,10 +89,13 @@ asmlinkage void do_sigreturn(struct pt_regs *regs)
sf = (struct signal_frame __user *) regs->u_regs[UREG_FP];

/* 1. Make sure we are not getting garbage from the user */
if (!access_ok(VERIFY_READ, sf, sizeof(*sf)))
if (!invalid_frame_pointer(sf, sizeof(*sf)))
goto segv_and_exit;

if (get_user(ufp, &sf->info.si_regs.u_regs[UREG_FP]))
goto segv_and_exit;

if (((unsigned long) sf) & 3)
if (ufp & 0x7)
goto segv_and_exit;

err = __get_user(pc, &sf->info.si_regs.pc);
Expand Down Expand Up @@ -127,16 +142,21 @@ asmlinkage void do_sigreturn(struct pt_regs *regs)
asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
{
struct rt_signal_frame __user *sf;
unsigned int psr, pc, npc;
unsigned int psr, pc, npc, ufp;
__siginfo_fpu_t __user *fpu_save;
__siginfo_rwin_t __user *rwin_save;
sigset_t set;
int err;

synchronize_user_stack();
sf = (struct rt_signal_frame __user *) regs->u_regs[UREG_FP];
if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) ||
(((unsigned long) sf) & 0x03))
if (!invalid_frame_pointer(sf, sizeof(*sf)))
goto segv;

if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
goto segv;

if (ufp & 0x7)
goto segv;

err = __get_user(pc, &sf->regs.pc);
Expand Down Expand Up @@ -178,15 +198,6 @@ asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
force_sig(SIGSEGV, current);
}

/* Checks if the fp is valid */
static inline int invalid_frame_pointer(void __user *fp, int fplen)
{
if ((((unsigned long) fp) & 7) || !__access_ok((unsigned long)fp, fplen))
return 1;

return 0;
}

static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize)
{
unsigned long sp = regs->u_regs[UREG_FP];
Expand Down
31 changes: 20 additions & 11 deletions arch/sparc/kernel/signal_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,17 @@ asmlinkage void sparc64_get_context(struct pt_regs *regs)
goto out;
}

/* Checks if the fp is valid. We always build rt signal frames which
* are 16-byte aligned, therefore we can always enforce that the
* restore frame has that property as well.
*/
static bool invalid_frame_pointer(void __user *fp)
{
if (((unsigned long) fp) & 15)
return true;
return false;
}

struct rt_signal_frame {
struct sparc_stackf ss;
siginfo_t info;
Expand All @@ -246,8 +257,8 @@ struct rt_signal_frame {

void do_rt_sigreturn(struct pt_regs *regs)
{
unsigned long tpc, tnpc, tstate, ufp;
struct rt_signal_frame __user *sf;
unsigned long tpc, tnpc, tstate;
__siginfo_fpu_t __user *fpu_save;
__siginfo_rwin_t __user *rwin_save;
sigset_t set;
Expand All @@ -261,10 +272,16 @@ void do_rt_sigreturn(struct pt_regs *regs)
(regs->u_regs [UREG_FP] + STACK_BIAS);

/* 1. Make sure we are not getting garbage from the user */
if (((unsigned long) sf) & 3)
if (invalid_frame_pointer(sf))
goto segv;

if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
goto segv;

err = get_user(tpc, &sf->regs.tpc);
if ((ufp + STACK_BIAS) & 0x7)
goto segv;

err = __get_user(tpc, &sf->regs.tpc);
err |= __get_user(tnpc, &sf->regs.tnpc);
if (test_thread_flag(TIF_32BIT)) {
tpc &= 0xffffffff;
Expand Down Expand Up @@ -308,14 +325,6 @@ void do_rt_sigreturn(struct pt_regs *regs)
force_sig(SIGSEGV, current);
}

/* Checks if the fp is valid */
static int invalid_frame_pointer(void __user *fp)
{
if (((unsigned long) fp) & 15)
return 1;
return 0;
}

static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize)
{
unsigned long sp = regs->u_regs[UREG_FP] + STACK_BIAS;
Expand Down
9 changes: 8 additions & 1 deletion arch/sparc/kernel/sigutil_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ int save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
{
int err;

if (((unsigned long) fpu) & 3)
return -EFAULT;

#ifdef CONFIG_SMP
if (test_tsk_thread_flag(current, TIF_USEDFPU))
regs->psr &= ~PSR_EF;
Expand Down Expand Up @@ -97,7 +101,10 @@ int restore_rwin_state(__siginfo_rwin_t __user *rp)
struct thread_info *t = current_thread_info();
int i, wsaved, err;

__get_user(wsaved, &rp->wsaved);
if (((unsigned long) rp) & 3)
return -EFAULT;

get_user(wsaved, &rp->wsaved);
if (wsaved > NSWINS)
return -EFAULT;

Expand Down
10 changes: 8 additions & 2 deletions arch/sparc/kernel/sigutil_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
unsigned long fprs;
int err;

err = __get_user(fprs, &fpu->si_fprs);
if (((unsigned long) fpu) & 7)
return -EFAULT;

err = get_user(fprs, &fpu->si_fprs);
fprs_write(0);
regs->tstate &= ~TSTATE_PEF;
if (fprs & FPRS_DL)
Expand Down Expand Up @@ -72,7 +75,10 @@ int restore_rwin_state(__siginfo_rwin_t __user *rp)
struct thread_info *t = current_thread_info();
int i, wsaved, err;

__get_user(wsaved, &rp->wsaved);
if (((unsigned long) rp) & 7)
return -EFAULT;

get_user(wsaved, &rp->wsaved);
if (wsaved > NSWINS)
return -EFAULT;

Expand Down

0 comments on commit 1fda90c

Please sign in to comment.