|
Message-ID: <CAGXu5jJd8-6BgDsyOAtftuCV5XWM1G1AMw5okYh5Y+Ld2YmTyw@mail.gmail.com> Date: Thu, 28 Jan 2016 15:31:45 -0800 From: Kees Cook <keescook@...omium.org> To: "kernel-hardening@...ts.openwall.com" <kernel-hardening@...ts.openwall.com> Cc: abhiram@...utah.edu, Scott Bauer <sbauer@....utah.edu> Subject: Re: [RFC PATCH 2/2] x86: SROP mitigation: implement signal cookies On Sat, Jan 23, 2016 at 11:59 PM, Scott Bauer <sbauer@....utah.edu> wrote: > This patch adds SROP mitigation logic to the x86 signal delivery > and sigreturn code. The cookie is placed in the unused alignment > space above the saved FP state, if it exists. If there is no FP > state to save then the cookie is placed in the alignment space above > the sigframe. > > Signed-off-by: Scott Bauer <sbauer@....utah.edu> > --- > arch/x86/ia32/ia32_signal.c | 63 +++++++++++++++++++++++++--- > arch/x86/include/asm/fpu/signal.h | 1 + > arch/x86/include/asm/sighandling.h | 5 ++- > arch/x86/kernel/fpu/signal.c | 12 ++++++ > arch/x86/kernel/signal.c | 86 +++++++++++++++++++++++++++++++++----- > 5 files changed, 148 insertions(+), 19 deletions(-) > > diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c > index 0552884..2751f47 100644 > --- a/arch/x86/ia32/ia32_signal.c > +++ b/arch/x86/ia32/ia32_signal.c > @@ -68,7 +68,8 @@ > } > > static int ia32_restore_sigcontext(struct pt_regs *regs, > - struct sigcontext_32 __user *sc) > + struct sigcontext_32 __user *sc, > + void __user **user_cookie) > { > unsigned int tmpflags, err = 0; > void __user *buf; > @@ -105,6 +106,16 @@ static int ia32_restore_sigcontext(struct pt_regs *regs, > buf = compat_ptr(tmp); > } get_user_catch(err); > > + /* > + * If there is fp state get cookie from the top of the fp state, > + * else get it from the top of the sig frame. > + */ > + > + if (tmp != 0) > + *user_cookie = compat_ptr(tmp + fpu__getsize(1)); > + else > + *user_cookie = NULL; > + > err |= fpu__restore_sig(buf, 1); > > force_iret(); > @@ -117,6 +128,7 @@ asmlinkage long sys32_sigreturn(void) > struct pt_regs *regs = current_pt_regs(); > struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8); > sigset_t set; > + void __user *user_cookie; > > if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) > goto badframe; > @@ -129,8 +141,15 @@ asmlinkage long sys32_sigreturn(void) > > set_current_blocked(&set); > > - if (ia32_restore_sigcontext(regs, &frame->sc)) > + if (ia32_restore_sigcontext(regs, &frame->sc, &user_cookie)) > + goto badframe; > + > + if (user_cookie == NULL) > + user_cookie = compat_ptr((regs->sp - 8) + sizeof(*frame)); > + > + if (verify_clear_sigcookie(user_cookie)) > goto badframe; > + > return regs->ax; > > badframe: > @@ -142,6 +161,7 @@ asmlinkage long sys32_rt_sigreturn(void) > { > struct pt_regs *regs = current_pt_regs(); > struct rt_sigframe_ia32 __user *frame; > + void __user *user_cookie; > sigset_t set; > > frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4); > @@ -153,7 +173,13 @@ asmlinkage long sys32_rt_sigreturn(void) > > set_current_blocked(&set); > > - if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext)) > + if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &user_cookie)) > + goto badframe; > + > + if (user_cookie == NULL) > + user_cookie = (void __user *)((regs->sp - 4) + sizeof(*frame)); > + > + if (verify_clear_sigcookie(user_cookie)) > goto badframe; > > if (compat_restore_altstack(&frame->uc.uc_stack)) > @@ -213,7 +239,8 @@ static int ia32_setup_sigcontext(struct sigcontext_32 __user *sc, > */ > static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, > size_t frame_size, > - void __user **fpstate) > + void __user **fpstate, > + void __user **cookie) > { > struct fpu *fpu = ¤t->thread.fpu; > unsigned long sp; > @@ -230,11 +257,21 @@ static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, > ksig->ka.sa.sa_restorer) > sp = (unsigned long) ksig->ka.sa.sa_restorer; > > + /* > + * Allocate space for cookie above FP/Frame. It will sit in > + * the padding of the saved FP state, or if there is no FP > + * state it will sit in the padding of the sig frame. > + */ > + sp -= sizeof(unsigned long); > + > + > if (fpu->fpstate_active) { > unsigned long fx_aligned, math_size; > > sp = fpu__alloc_mathframe(sp, 1, &fx_aligned, &math_size); > *fpstate = (struct _fpstate_32 __user *) sp; > + *cookie = (void __user *)sp + math_size; > + > if (copy_fpstate_to_sigframe(*fpstate, (void __user *)fx_aligned, > math_size) < 0) > return (void __user *) -1L; > @@ -244,6 +281,10 @@ static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, > /* Align the stack pointer according to the i386 ABI, > * i.e. so that on function entry ((sp + 4) & 15) == 0. */ > sp = ((sp + 4) & -16ul) - 4; > + > + if (!fpu->fpstate_active) > + *cookie = (void __user *) (sp + frame_size); > + > return (void __user *) sp; > } > > @@ -254,6 +295,7 @@ int ia32_setup_frame(int sig, struct ksignal *ksig, > void __user *restorer; > int err = 0; > void __user *fpstate = NULL; > + void __user *cookie_location; > > /* copy_to_user optimizes that into a single 8 byte store */ > static const struct { > @@ -266,7 +308,8 @@ int ia32_setup_frame(int sig, struct ksignal *ksig, > 0x80cd, /* int $0x80 */ > }; > > - frame = get_sigframe(ksig, regs, sizeof(*frame), &fpstate); > + frame = get_sigframe(ksig, regs, sizeof(*frame), > + &fpstate, &cookie_location); > > if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) > return -EFAULT; > @@ -274,6 +317,9 @@ int ia32_setup_frame(int sig, struct ksignal *ksig, > if (__put_user(sig, &frame->sig)) > return -EFAULT; > > + if (set_sigcookie(cookie_location)) > + return -EFAULT; > + > if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0])) > return -EFAULT; > > @@ -332,6 +378,7 @@ int ia32_setup_rt_frame(int sig, struct ksignal *ksig, > void __user *restorer; > int err = 0; > void __user *fpstate = NULL; > + void __user *cookie_location; > > /* __copy_to_user optimizes that into a single 8 byte store */ > static const struct { > @@ -346,11 +393,15 @@ int ia32_setup_rt_frame(int sig, struct ksignal *ksig, > 0, > }; > > - frame = get_sigframe(ksig, regs, sizeof(*frame), &fpstate); > + frame = get_sigframe(ksig, regs, sizeof(*frame), > + &fpstate, &cookie_location); > > if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) > return -EFAULT; > > + if (set_sigcookie(cookie_location)) > + return -EFAULT; > + > put_user_try { > put_user_ex(sig, &frame->sig); > put_user_ex(ptr_to_compat(&frame->info), &frame->pinfo); > diff --git a/arch/x86/include/asm/fpu/signal.h b/arch/x86/include/asm/fpu/signal.h > index 0e970d0..a720b95 100644 > --- a/arch/x86/include/asm/fpu/signal.h > +++ b/arch/x86/include/asm/fpu/signal.h > @@ -27,6 +27,7 @@ extern void convert_to_fxsr(struct task_struct *tsk, > unsigned long > fpu__alloc_mathframe(unsigned long sp, int ia32_frame, > unsigned long *buf_fx, unsigned long *size); > +unsigned long fpu__getsize(int ia32_frame); > > extern void fpu__init_prepare_fx_sw_frame(void); > > diff --git a/arch/x86/include/asm/sighandling.h b/arch/x86/include/asm/sighandling.h > index 89db467..971c8b2 100644 > --- a/arch/x86/include/asm/sighandling.h > +++ b/arch/x86/include/asm/sighandling.h > @@ -12,8 +12,9 @@ > X86_EFLAGS_ZF | X86_EFLAGS_AF | X86_EFLAGS_PF | \ > X86_EFLAGS_CF | X86_EFLAGS_RF) > > -void signal_fault(struct pt_regs *regs, void __user *frame, char *where); > -int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc); > +void signal_fault(struct pt_regs *regs, void __user *frame, const char *where); > +int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, > + void __user **user_cookie); > int setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate, > struct pt_regs *regs, unsigned long mask); > > diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c > index 31c6a60..35b6b2d 100644 > --- a/arch/x86/kernel/fpu/signal.c > +++ b/arch/x86/kernel/fpu/signal.c > @@ -344,6 +344,16 @@ static inline int xstate_sigframe_size(void) > return use_xsave() ? xstate_size + FP_XSTATE_MAGIC2_SIZE : xstate_size; > } > > +unsigned long fpu__getsize(int ia32_frame) > +{ > + unsigned long frame_size = xstate_sigframe_size(); > + > + if (ia32_frame && use_fxsr()) > + frame_size += sizeof(struct fregs_state); > + > + return frame_size; > +} > + > /* > * Restore FPU state from a sigframe: > */ > @@ -360,6 +370,8 @@ int fpu__restore_sig(void __user *buf, int ia32_frame) > return __fpu__restore_sig(buf, buf_fx, size); > } > > + > + > unsigned long > fpu__alloc_mathframe(unsigned long sp, int ia32_frame, > unsigned long *buf_fx, unsigned long *size) Needless whitespace addition can be dropped above... -Kees > diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c > index cb6282c..14d3103 100644 > --- a/arch/x86/kernel/signal.c > +++ b/arch/x86/kernel/signal.c > @@ -23,6 +23,7 @@ > #include <linux/user-return-notifier.h> > #include <linux/uprobes.h> > #include <linux/context_tracking.h> > +#include <linux/hash.h> > > #include <asm/processor.h> > #include <asm/ucontext.h> > @@ -61,7 +62,9 @@ > regs->seg = GET_SEG(seg) | 3; \ > } while (0) > > -int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) > + > +int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, > + void __user **user_cookie) > { > unsigned long buf_val; > void __user *buf; > @@ -112,8 +115,14 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) > buf = (void __user *)buf_val; > } get_user_catch(err); > > - err |= fpu__restore_sig(buf, config_enabled(CONFIG_X86_32)); > > + if (buf_val != 0) > + *user_cookie = (void __user *) > + (buf_val + fpu__getsize(config_enabled(CONFIG_X86_32))); > + else > + *user_cookie = NULL; > + > + err |= fpu__restore_sig(buf, config_enabled(CONFIG_X86_32)); > force_iret(); > > return err; > @@ -200,7 +209,7 @@ static unsigned long align_sigframe(unsigned long sp) > > static void __user * > get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, > - void __user **fpstate) > + void __user **fpstate, void __user **cookie) > { > /* Default to using normal stack */ > unsigned long math_size = 0; > @@ -227,14 +236,27 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, > } > } > > + > + /* > + * Allocate space for cookie above FP/Frame. It will sit in > + * the padding of the saved FP state, or if there is no FP > + * state it will sit in the padding of the sig frame. > + */ > + sp -= sizeof(unsigned long); > + > if (fpu->fpstate_active) { > sp = fpu__alloc_mathframe(sp, config_enabled(CONFIG_X86_32), > &buf_fx, &math_size); > *fpstate = (void __user *)sp; > + *cookie = (void __user *)sp + math_size; > } > > sp = align_sigframe(sp - frame_size); > > + if (!fpu->fpstate_active) > + *cookie = (void __user *) (sp + frame_size); > + > + > /* > * If we are on the alternate signal stack and would overflow it, don't. > * Return an always-bogus address instead so we will die with SIGSEGV. > @@ -281,8 +303,10 @@ __setup_frame(int sig, struct ksignal *ksig, sigset_t *set, > void __user *restorer; > int err = 0; > void __user *fpstate = NULL; > + void __user *cookie_location; > > - frame = get_sigframe(&ksig->ka, regs, sizeof(*frame), &fpstate); > + frame = get_sigframe(&ksig->ka, regs, sizeof(*frame), > + &fpstate, &cookie_location); > > if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) > return -EFAULT; > @@ -290,6 +314,9 @@ __setup_frame(int sig, struct ksignal *ksig, sigset_t *set, > if (__put_user(sig, &frame->sig)) > return -EFAULT; > > + if (set_sigcookie(cookie_location)) > + return -EFAULT; > + > if (setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0])) > return -EFAULT; > > @@ -344,12 +371,17 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig, > void __user *restorer; > int err = 0; > void __user *fpstate = NULL; > + void __user *cookie_location; > > - frame = get_sigframe(&ksig->ka, regs, sizeof(*frame), &fpstate); > + frame = get_sigframe(&ksig->ka, regs, sizeof(*frame), > + &fpstate, &cookie_location); > > if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) > return -EFAULT; > > + if (set_sigcookie(cookie_location)) > + return -EFAULT; > + > put_user_try { > put_user_ex(sig, &frame->sig); > put_user_ex(&frame->info, &frame->pinfo); > @@ -408,9 +440,11 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig, > { > struct rt_sigframe __user *frame; > void __user *fp = NULL; > + void __user *cookie_location; > int err = 0; > > - frame = get_sigframe(&ksig->ka, regs, sizeof(struct rt_sigframe), &fp); > + frame = get_sigframe(&ksig->ka, regs, sizeof(*frame), > + &fp, &cookie_location); > > if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) > return -EFAULT; > @@ -420,6 +454,9 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig, > return -EFAULT; > } > > + if (set_sigcookie(cookie_location)) > + return -EFAULT; > + > put_user_try { > /* Create the ucontext. */ > if (cpu_has_xsave) > @@ -476,8 +513,10 @@ static int x32_setup_rt_frame(struct ksignal *ksig, > void __user *restorer; > int err = 0; > void __user *fpstate = NULL; > + void __user *cookie_location; > > - frame = get_sigframe(&ksig->ka, regs, sizeof(*frame), &fpstate); > + frame = get_sigframe(&ksig->ka, regs, sizeof(*frame), > + &fpstate, &cookie_location); > > if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) > return -EFAULT; > @@ -487,6 +526,9 @@ static int x32_setup_rt_frame(struct ksignal *ksig, > return -EFAULT; > } > > + if (set_sigcookie(cookie_location)) > + return -EFAULT; > + > put_user_try { > /* Create the ucontext. */ > if (cpu_has_xsave) > @@ -541,6 +583,7 @@ asmlinkage unsigned long sys_sigreturn(void) > { > struct pt_regs *regs = current_pt_regs(); > struct sigframe __user *frame; > + void __user *user_cookie; > sigset_t set; > > frame = (struct sigframe __user *)(regs->sp - 8); > @@ -554,8 +597,15 @@ asmlinkage unsigned long sys_sigreturn(void) > > set_current_blocked(&set); > > - if (restore_sigcontext(regs, &frame->sc)) > + if (restore_sigcontext(regs, &frame->sc, &user_cookie)) > + goto badframe; > + > + if (user_cookie == NULL) > + user_cookie = (void __user *) ((regs->sp - 8) + sizeof(*frame)); > + > + if (verify_clear_sigcookie(user_cookie)) > goto badframe; > + > return regs->ax; > > badframe: > @@ -569,6 +619,7 @@ asmlinkage long sys_rt_sigreturn(void) > { > struct pt_regs *regs = current_pt_regs(); > struct rt_sigframe __user *frame; > + void __user *user_cookie; > sigset_t set; > > frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long)); > @@ -579,7 +630,13 @@ asmlinkage long sys_rt_sigreturn(void) > > set_current_blocked(&set); > > - if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) > + if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &user_cookie)) > + goto badframe; > + > + if (user_cookie == NULL) > + user_cookie = (void __user *) ((regs->sp - 8) + sizeof(*frame)); > + > + if (verify_clear_sigcookie(user_cookie)) > goto badframe; > > if (restore_altstack(&frame->uc.uc_stack)) > @@ -740,7 +797,7 @@ void do_signal(struct pt_regs *regs) > restore_saved_sigmask(); > } > > -void signal_fault(struct pt_regs *regs, void __user *frame, char *where) > +void signal_fault(struct pt_regs *regs, void __user *frame, const char *where) > { > struct task_struct *me = current; > > @@ -762,6 +819,7 @@ asmlinkage long sys32_x32_rt_sigreturn(void) > { > struct pt_regs *regs = current_pt_regs(); > struct rt_sigframe_x32 __user *frame; > + void __user *user_cookie; > sigset_t set; > > frame = (struct rt_sigframe_x32 __user *)(regs->sp - 8); > @@ -773,7 +831,13 @@ asmlinkage long sys32_x32_rt_sigreturn(void) > > set_current_blocked(&set); > > - if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) > + if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &user_cookie)) > + goto badframe; > + > + if (user_cookie == NULL) > + user_cookie = (void __user *) ((regs->sp - 8) + sizeof(*frame)); > + > + if (verify_clear_sigcookie(user_cookie)) > goto badframe; > > if (compat_restore_altstack(&frame->uc.uc_stack)) > -- > 1.9.1 > -- Kees Cook Chrome OS & Brillo Security
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.