mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 14:42:24 +00:00
sparc: Harden signal return frame checks.
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>
This commit is contained in:
parent
9ea46abe22
commit
d11c2a0de2
@ -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 int pc, npc;
|
||||
sigset_t set;
|
||||
compat_sigset_t seta;
|
||||
@ -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(pc, &sf->info.si_regs.pc) ||
|
||||
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) ||
|
||||
__get_user(npc, &sf->info.si_regs.npc))
|
||||
goto segv;
|
||||
|
||||
@ -227,7 +244,7 @@ segv:
|
||||
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;
|
||||
@ -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;
|
||||
|
||||
@ -307,14 +329,6 @@ segv:
|
||||
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;
|
||||
|
@ -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;
|
||||
@ -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 (((unsigned long) sf) & 3)
|
||||
if (get_user(ufp, &sf->info.si_regs.u_regs[UREG_FP]))
|
||||
goto segv_and_exit;
|
||||
|
||||
if (ufp & 0x7)
|
||||
goto segv_and_exit;
|
||||
|
||||
err = __get_user(pc, &sf->info.si_regs.pc);
|
||||
@ -127,7 +142,7 @@ segv_and_exit:
|
||||
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;
|
||||
@ -135,8 +150,13 @@ asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
|
||||
|
||||
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);
|
||||
@ -178,15 +198,6 @@ segv:
|
||||
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];
|
||||
|
@ -234,6 +234,17 @@ do_sigsegv:
|
||||
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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
|
||||
err = get_user(tpc, &sf->regs.tpc);
|
||||
if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
|
||||
goto segv;
|
||||
|
||||
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;
|
||||
@ -308,14 +325,6 @@ segv:
|
||||
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;
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user