Re: [PATCH v4 23/30] riscv signal: save and restore of shadow stack for signal
Deepak Gupta 於 2024年9月17日 週二 上午12:03寫道: > > On Fri, Sep 13, 2024 at 09:25:57PM +0200, Andy Chiu wrote: > >Hi Deepak, > > > >Deepak Gupta 於 2024年9月13日 週五 上午1:20寫道: > >> > >> Save shadow stack pointer in sigcontext structure while delivering signal. > >> Restore shadow stack pointer from sigcontext on sigreturn. > >> > >> As part of save operation, kernel uses `ssamoswap` to save snapshot of > >> current shadow stack on shadow stack itself (can be called as a save > >> token). During restore on sigreturn, kernel retrieves token from top of > >> shadow stack and validates it. This allows that user mode can't arbitrary > >> pivot to any shadow stack address without having a token and thus provide > >> strong security assurance between signaly delivery and sigreturn window. > >> > >> Signed-off-by: Deepak Gupta > >> Suggested-by: Andy Chiu > >> --- > >> arch/riscv/include/asm/usercfi.h | 19 ++ > >> arch/riscv/kernel/signal.c | 62 +++- > >> arch/riscv/kernel/usercfi.c | 57 + > >> 3 files changed, 137 insertions(+), 1 deletion(-) > >> > >> diff --git a/arch/riscv/include/asm/usercfi.h > >> b/arch/riscv/include/asm/usercfi.h > >> index 20a9102cce51..d5050a5df26c 100644 > >> --- a/arch/riscv/include/asm/usercfi.h > >> +++ b/arch/riscv/include/asm/usercfi.h > >> @@ -8,6 +8,7 @@ > >> #ifndef __ASSEMBLY__ > >> #include > >> #include > >> +#include > >> > >> struct task_struct; > >> struct kernel_clone_args; > >> @@ -35,6 +36,9 @@ bool is_shstk_locked(struct task_struct *task); > >> bool is_shstk_allocated(struct task_struct *task); > >> void set_shstk_lock(struct task_struct *task); > >> void set_shstk_status(struct task_struct *task, bool enable); > >> +unsigned long get_active_shstk(struct task_struct *task); > >> +int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr); > >> +int save_user_shstk(struct task_struct *tsk, unsigned long > >> *saved_shstk_ptr); > >> bool is_indir_lp_enabled(struct task_struct *task); > >> bool is_indir_lp_locked(struct task_struct *task); > >> void set_indir_lp_status(struct task_struct *task, bool enable); > >> @@ -96,6 +100,21 @@ static inline void set_shstk_status(struct task_struct > >> *task, bool enable) > >> > >> } > >> > >> +static inline int restore_user_shstk(struct task_struct *tsk, unsigned > >> long shstk_ptr) > >> +{ > >> + return -EINVAL; > >> +} > >> + > >> +static inline int save_user_shstk(struct task_struct *tsk, unsigned long > >> *saved_shstk_ptr) > >> +{ > >> + return -EINVAL; > >> +} > >> + > >> +static inline unsigned long get_active_shstk(struct task_struct *task) > >> +{ > >> + return 0; > >> +} > >> + > >> static inline bool is_indir_lp_enabled(struct task_struct *task) > >> { > >> return false; > >> diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c > >> index dcd282419456..7d5c1825650f 100644 > >> --- a/arch/riscv/kernel/signal.c > >> +++ b/arch/riscv/kernel/signal.c > >> @@ -22,6 +22,7 @@ > >> #include > >> #include > >> #include > >> +#include > >> > >> unsigned long signal_minsigstksz __ro_after_init; > >> > >> @@ -153,6 +154,16 @@ static long restore_sigcontext(struct pt_regs *regs, > >> void __user *sc_ext_ptr = &sc->sc_extdesc.hdr; > >> __u32 rsvd; > >> long err; > >> + unsigned long ss_ptr = 0; > >> + struct __sc_riscv_cfi_state __user *sc_cfi = NULL; > >> + > >> + sc_cfi = (struct __sc_riscv_cfi_state *) > >> +((unsigned long) sc_ext_ptr + sizeof(struct > >> __riscv_ctx_hdr)); > >> + > >> + if (has_vector() && riscv_v_vstate_query(regs)) > >> + sc_cfi = (struct __sc_riscv_cfi_state *) > >> +((unsigned long) sc_cfi + riscv_v_sc_size); > >> + > >> /* sc_regs is structured the same as the start of pt_regs */ > >> err = __copy_from_user(regs, &sc->sc_regs, sizeof(sc->sc_regs)); > >> if (unlikely(err)) > >> @@ -172,6 +183,24 @@ static long restore_sigcontext(struct pt_regs *regs, > >> if (unlikely(rsvd)) > >> return -EINVAL; > >> > >> + /* > >> +* Restore shadow stack as a form of token stored on shadow stack > >> itself as a safe > >> +* way to restore. > >> +* A token on shadow gives following properties > >> +* - Safe save and restore for shadow stack switching. Any > >> save of shadow stack > >> +*must have had saved a token on shadow stack. Similarly > >> any restore of shadow > >> +*stack must check the token before restore. Since writing > >> to shadow stack with > >> +*address of shadow stack itself is not easily allowed. A > >> restore without a save > >> +*is quite difficult for an attacker to perform. > >> +* - A natural break. A token in shadow stack provides a > >> natural break in shadow stack > >>
Re: [PATCH v4 23/30] riscv signal: save and restore of shadow stack for signal
On Wed, Sep 18, 2024 at 12:03:45AM +0200, Andy Chiu wrote: Deepak Gupta 於 2024年9月17日 週二 上午12:03寫道: On Fri, Sep 13, 2024 at 09:25:57PM +0200, Andy Chiu wrote: >Hi Deepak, > >Deepak Gupta 於 2024年9月13日 週五 上午1:20寫道: >> >> Save shadow stack pointer in sigcontext structure while delivering signal. >> Restore shadow stack pointer from sigcontext on sigreturn. >> >> As part of save operation, kernel uses `ssamoswap` to save snapshot of >> current shadow stack on shadow stack itself (can be called as a save >> token). During restore on sigreturn, kernel retrieves token from top of >> shadow stack and validates it. This allows that user mode can't arbitrary >> pivot to any shadow stack address without having a token and thus provide >> strong security assurance between signaly delivery and sigreturn window. >> >> Signed-off-by: Deepak Gupta >> Suggested-by: Andy Chiu >> --- >> arch/riscv/include/asm/usercfi.h | 19 ++ >> arch/riscv/kernel/signal.c | 62 +++- >> arch/riscv/kernel/usercfi.c | 57 + >> 3 files changed, 137 insertions(+), 1 deletion(-) >> >> diff --git a/arch/riscv/include/asm/usercfi.h b/arch/riscv/include/asm/usercfi.h >> index 20a9102cce51..d5050a5df26c 100644 >> --- a/arch/riscv/include/asm/usercfi.h >> +++ b/arch/riscv/include/asm/usercfi.h >> @@ -8,6 +8,7 @@ >> #ifndef __ASSEMBLY__ >> #include >> #include >> +#include >> >> struct task_struct; >> struct kernel_clone_args; >> @@ -35,6 +36,9 @@ bool is_shstk_locked(struct task_struct *task); >> bool is_shstk_allocated(struct task_struct *task); >> void set_shstk_lock(struct task_struct *task); >> void set_shstk_status(struct task_struct *task, bool enable); >> +unsigned long get_active_shstk(struct task_struct *task); >> +int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr); >> +int save_user_shstk(struct task_struct *tsk, unsigned long *saved_shstk_ptr); >> bool is_indir_lp_enabled(struct task_struct *task); >> bool is_indir_lp_locked(struct task_struct *task); >> void set_indir_lp_status(struct task_struct *task, bool enable); >> @@ -96,6 +100,21 @@ static inline void set_shstk_status(struct task_struct *task, bool enable) >> >> } >> >> +static inline int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr) >> +{ >> + return -EINVAL; >> +} >> + >> +static inline int save_user_shstk(struct task_struct *tsk, unsigned long *saved_shstk_ptr) >> +{ >> + return -EINVAL; >> +} >> + >> +static inline unsigned long get_active_shstk(struct task_struct *task) >> +{ >> + return 0; >> +} >> + >> static inline bool is_indir_lp_enabled(struct task_struct *task) >> { >> return false; >> diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c >> index dcd282419456..7d5c1825650f 100644 >> --- a/arch/riscv/kernel/signal.c >> +++ b/arch/riscv/kernel/signal.c >> @@ -22,6 +22,7 @@ >> #include >> #include >> #include >> +#include >> >> unsigned long signal_minsigstksz __ro_after_init; >> >> @@ -153,6 +154,16 @@ static long restore_sigcontext(struct pt_regs *regs, >> void __user *sc_ext_ptr = &sc->sc_extdesc.hdr; >> __u32 rsvd; >> long err; >> + unsigned long ss_ptr = 0; >> + struct __sc_riscv_cfi_state __user *sc_cfi = NULL; >> + >> + sc_cfi = (struct __sc_riscv_cfi_state *) >> +((unsigned long) sc_ext_ptr + sizeof(struct __riscv_ctx_hdr)); >> + >> + if (has_vector() && riscv_v_vstate_query(regs)) >> + sc_cfi = (struct __sc_riscv_cfi_state *) >> +((unsigned long) sc_cfi + riscv_v_sc_size); >> + >> /* sc_regs is structured the same as the start of pt_regs */ >> err = __copy_from_user(regs, &sc->sc_regs, sizeof(sc->sc_regs)); >> if (unlikely(err)) >> @@ -172,6 +183,24 @@ static long restore_sigcontext(struct pt_regs *regs, >> if (unlikely(rsvd)) >> return -EINVAL; >> >> + /* >> +* Restore shadow stack as a form of token stored on shadow stack itself as a safe >> +* way to restore. >> +* A token on shadow gives following properties >> +* - Safe save and restore for shadow stack switching. Any save of shadow stack >> +*must have had saved a token on shadow stack. Similarly any restore of shadow >> +*stack must check the token before restore. Since writing to shadow stack with >> +*address of shadow stack itself is not easily allowed. A restore without a save >> +*is quite difficult for an attacker to perform. >> +* - A natural break. A token in shadow stack provides a natural break in shadow stack >> +*So a single linear range can be bucketed into different shadow stack segments. >> +*sspopchk will detect the condition and fault to kernel as sw check exception. >> +*/ >> +