Re: [PATCH v4 23/30] riscv signal: save and restore of shadow stack for signal

2024-09-17 Thread Andy Chiu
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

2024-09-17 Thread Deepak Gupta

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.
>> +*/
>> +