Re: [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack

2023-02-15 Thread Deepak Gupta
On Wed, Feb 15, 2023 at 6:36 PM LIU Zhiwei  wrote:
>
>
> On 2023/2/16 7:57, Deepak Gupta wrote:
>
> `On Wed, Feb 15, 2023 at 12:43 AM LIU Zhiwei
>  wrote:
>
> On 2023/2/9 14:24, Deepak Gupta wrote:
>
> zisslpcfi protects returns(back cfi) using shadow stack. If compiled with
> enabled compiler, function prologs will have `sspush ra` instruction to
> push return address on shadow stack and function epilogs will have
> `sspop t0; sschckra` instruction sequences. `sspop t0` will pop the
> value from top of the shadow stack in t0. `sschckra` will compare `t0`
> and `x1` and if they don't match then hart will raise an illegal
> instruction exception.
>
> Shadow stack is read-only memory except stores can be performed via
> `sspush` and `ssamoswap` instructions. This requires new PTE encoding for
> shadow stack. zisslpcfi uses R=0, W=1, X=0 (an existing reserved encoding
> ) to encode a shadow stack. If backward cfi is not enabled for current
> mode, shadow stack PTE encodings remain reserved. Regular stores to
> shadow stack raise AMO/store access fault. Shadow stack loads/stores on
> regular memory raise load access/store access fault.
>
> This patch creates a new MMU TLB index for shadow stack and flushes TLB
> for shadow stack on privileges changes. This patch doesn't implement
> `Smepmp` related enforcement on shadow stack pmp entry. Reason being qemu
> doesn't have `Smepmp` implementation yet.
>
> I don't know that the Smepmp means here. QEMU has supported the epmp.
>
> https://github.com/riscv/riscv-tee/blob/main/Smepmp/Smepmp.pdf
>
> This specification has been supported. You can enable this extension by -cpu 
> rv64,x-epmp=on.
>
> I didn't see the special contents for shadow stack, neither on cfi 
> specfication nor the epmp specification.
>
> You should make it clear that how the shadow stack influenced by the pmp 
> rules.

Aah cool. I didn't notice that. I was looking for the string `smepmp`.

WIthout paging, shadow stack store/load simply honor pmpcfg read/write
permissions.
`sspush/ssamoswap` will succeed if the selected pmp entry's pmpcfg
allows a write. `sspop/ssamoswap` will
succeed if pmpcfg allows a read.

For M-mode shadow stack, there is a 6 bit field `sspmp` in mseccfg which selects
pmp entry for shadow stack accesses in M mode

M mode protection of shadow stack:
When mseccfg.MML=1, then
- sspush/sspop/ssamoswap must match PMP entry pointed to by `sspmp`
- stores other than `sspush` and `ssamoswap` that match pmp entry
`sspmp` must can access violation

I've to implement mseccfg.MML=1 rule for shadow stack in M mode.

Note: W=1,R=0,X=0 has already been claimed by Smepmp. So we can't use
that for the shadow stack in pmpcfg.

>
>   `Smepmp` enforcement should come
> whenever it is implemented.
>
> Signed-off-by: Deepak Gupta 
> Signed-off-by: Kip Walker  
> ---
>   target/riscv/cpu-param.h  |   1 +
>   target/riscv/cpu.c|   2 +
>   target/riscv/cpu.h|   3 ++
>   target/riscv/cpu_helper.c | 107 +++---
>   4 files changed, 94 insertions(+), 19 deletions(-)
>
> diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h
> index ebaf26d26d..a1e379beb7 100644
> --- a/target/riscv/cpu-param.h
> +++ b/target/riscv/cpu-param.h
> @@ -25,6 +25,7 @@
>*  - M mode 0b011
>*  - U mode HLV/HLVX/HSV 0b100
>*  - S mode HLV/HLVX/HSV 0b101
> + *  - BCFI shadow stack   0b110
>*  - M mode HLV/HLVX/HSV 0b111
>*/
>   #define NB_MMU_MODES 8
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 6b4e90eb91..14cfb93288 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -584,6 +584,8 @@ static void riscv_cpu_reset_hold(Object *obj)
>   }
>   /* mmte is supposed to have pm.current hardwired to 1 */
>   env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
> +/* Initialize ss_priv to current priv. */
> +env->ss_priv = env->priv;
>   #endif
>   env->xl = riscv_cpu_mxl(env);
>   riscv_cpu_update_mask(env);
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index d14ea4f91d..8803ea6426 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -379,6 +379,7 @@ struct CPUArchState {
>   uint64_t sstateen[SMSTATEEN_MAX_COUNT];
>   target_ulong senvcfg;
>   uint64_t henvcfg;
> +target_ulong ss_priv;
>   #endif
>   target_ulong cur_pmmask;
>   target_ulong cur_pmbase;
> @@ -617,6 +618,8 @@ void riscv_cpu_set_fflags(CPURISCVState *env, 
> target_ulong);
>   #define TB_FLAGS_PRIV_HYP_ACCESS_MASK   (1 << 2)
>   #define TB_FLAGS_MSTATUS_FS MSTATUS_FS
>   #define TB_FLAGS_MSTATUS_VS MSTATUS_VS
> +/* TLB MMU index for shadow stack accesses */
> +#define MMU_IDX_SS_ACCESS6
>
>   #include "exec/cpu-all.h"
>
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index fc188683c9..63377abc2f 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -657,7 +657,8 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool 
> enable)
>
>   bool

Re: [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack

2023-02-15 Thread LIU Zhiwei


On 2023/2/16 7:57, Deepak Gupta wrote:

`On Wed, Feb 15, 2023 at 12:43 AM LIU Zhiwei
  wrote:


On 2023/2/9 14:24, Deepak Gupta wrote:

zisslpcfi protects returns(back cfi) using shadow stack. If compiled with
enabled compiler, function prologs will have `sspush ra` instruction to
push return address on shadow stack and function epilogs will have
`sspop t0; sschckra` instruction sequences. `sspop t0` will pop the
value from top of the shadow stack in t0. `sschckra` will compare `t0`
and `x1` and if they don't match then hart will raise an illegal
instruction exception.

Shadow stack is read-only memory except stores can be performed via
`sspush` and `ssamoswap` instructions. This requires new PTE encoding for
shadow stack. zisslpcfi uses R=0, W=1, X=0 (an existing reserved encoding
) to encode a shadow stack. If backward cfi is not enabled for current
mode, shadow stack PTE encodings remain reserved. Regular stores to
shadow stack raise AMO/store access fault. Shadow stack loads/stores on
regular memory raise load access/store access fault.

This patch creates a new MMU TLB index for shadow stack and flushes TLB
for shadow stack on privileges changes. This patch doesn't implement
`Smepmp` related enforcement on shadow stack pmp entry. Reason being qemu
doesn't have `Smepmp` implementation yet.

I don't know that the Smepmp means here. QEMU has supported the epmp.

https://github.com/riscv/riscv-tee/blob/main/Smepmp/Smepmp.pdf


This specification has been supported. You can enable this extension by 
-cpu rv64,x-epmp=on.


I didn't see the special contents for shadow stack, neither on cfi 
specfication nor the epmp specification.


You should make it clear that how the shadow stack influenced by the pmp 
rules.





   `Smepmp` enforcement should come
whenever it is implemented.

Signed-off-by: Deepak Gupta
Signed-off-by: Kip Walker
---
   target/riscv/cpu-param.h  |   1 +
   target/riscv/cpu.c|   2 +
   target/riscv/cpu.h|   3 ++
   target/riscv/cpu_helper.c | 107 +++---
   4 files changed, 94 insertions(+), 19 deletions(-)

diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h
index ebaf26d26d..a1e379beb7 100644
--- a/target/riscv/cpu-param.h
+++ b/target/riscv/cpu-param.h
@@ -25,6 +25,7 @@
*  - M mode 0b011
*  - U mode HLV/HLVX/HSV 0b100
*  - S mode HLV/HLVX/HSV 0b101
+ *  - BCFI shadow stack   0b110
*  - M mode HLV/HLVX/HSV 0b111
*/
   #define NB_MMU_MODES 8
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 6b4e90eb91..14cfb93288 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -584,6 +584,8 @@ static void riscv_cpu_reset_hold(Object *obj)
   }
   /* mmte is supposed to have pm.current hardwired to 1 */
   env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
+/* Initialize ss_priv to current priv. */
+env->ss_priv = env->priv;
   #endif
   env->xl = riscv_cpu_mxl(env);
   riscv_cpu_update_mask(env);
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index d14ea4f91d..8803ea6426 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -379,6 +379,7 @@ struct CPUArchState {
   uint64_t sstateen[SMSTATEEN_MAX_COUNT];
   target_ulong senvcfg;
   uint64_t henvcfg;
+target_ulong ss_priv;
   #endif
   target_ulong cur_pmmask;
   target_ulong cur_pmbase;
@@ -617,6 +618,8 @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
   #define TB_FLAGS_PRIV_HYP_ACCESS_MASK   (1 << 2)
   #define TB_FLAGS_MSTATUS_FS MSTATUS_FS
   #define TB_FLAGS_MSTATUS_VS MSTATUS_VS
+/* TLB MMU index for shadow stack accesses */
+#define MMU_IDX_SS_ACCESS6

   #include "exec/cpu-all.h"

diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index fc188683c9..63377abc2f 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -657,7 +657,8 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool 
enable)

   bool riscv_cpu_two_stage_lookup(int mmu_idx)
   {
-return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK;
+return (mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK) &&
+   (mmu_idx != MMU_IDX_SS_ACCESS);
   }

   int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
@@ -745,6 +746,38 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong 
newpriv)
* preemptive context switch. As a result, do both.
*/
   env->load_res = -1;
+
+if (cpu_get_bcfien(env) && (env->priv != env->ss_priv)) {
+/*
+ * If backward CFI is enabled in the new privilege state, the
+ * shadow stack TLB needs to be flushed - unless the most recent
+ * use of the SS TLB was for the same privilege mode.
+ */
+tlb_flush_by_mmuidx(env_cpu(env), 1 << MMU_IDX_SS_ACCESS);
+/*
+ * Ignoring env->virt here since currently every time it flips,
+ * all TLBs are flushed anyway.
+ */
+env->ss_priv = env->priv;
+}
+}
+
+typedef enum {
+SSTACK_NO,   

Re: [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack

2023-02-15 Thread Deepak Gupta
`On Wed, Feb 15, 2023 at 12:43 AM LIU Zhiwei
 wrote:
>
>
> On 2023/2/9 14:24, Deepak Gupta wrote:
> > zisslpcfi protects returns(back cfi) using shadow stack. If compiled with
> > enabled compiler, function prologs will have `sspush ra` instruction to
> > push return address on shadow stack and function epilogs will have
> > `sspop t0; sschckra` instruction sequences. `sspop t0` will pop the
> > value from top of the shadow stack in t0. `sschckra` will compare `t0`
> > and `x1` and if they don't match then hart will raise an illegal
> > instruction exception.
> >
> > Shadow stack is read-only memory except stores can be performed via
> > `sspush` and `ssamoswap` instructions. This requires new PTE encoding for
> > shadow stack. zisslpcfi uses R=0, W=1, X=0 (an existing reserved encoding
> > ) to encode a shadow stack. If backward cfi is not enabled for current
> > mode, shadow stack PTE encodings remain reserved. Regular stores to
> > shadow stack raise AMO/store access fault. Shadow stack loads/stores on
> > regular memory raise load access/store access fault.
> >
> > This patch creates a new MMU TLB index for shadow stack and flushes TLB
> > for shadow stack on privileges changes. This patch doesn't implement
> > `Smepmp` related enforcement on shadow stack pmp entry. Reason being qemu
> > doesn't have `Smepmp` implementation yet.
> I don't know that the Smepmp means here. QEMU has supported the epmp.

https://github.com/riscv/riscv-tee/blob/main/Smepmp/Smepmp.pdf

> >   `Smepmp` enforcement should come
> > whenever it is implemented.
> >
> > Signed-off-by: Deepak Gupta 
> > Signed-off-by: Kip Walker  
> > ---
> >   target/riscv/cpu-param.h  |   1 +
> >   target/riscv/cpu.c|   2 +
> >   target/riscv/cpu.h|   3 ++
> >   target/riscv/cpu_helper.c | 107 +++---
> >   4 files changed, 94 insertions(+), 19 deletions(-)
> >
> > diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h
> > index ebaf26d26d..a1e379beb7 100644
> > --- a/target/riscv/cpu-param.h
> > +++ b/target/riscv/cpu-param.h
> > @@ -25,6 +25,7 @@
> >*  - M mode 0b011
> >*  - U mode HLV/HLVX/HSV 0b100
> >*  - S mode HLV/HLVX/HSV 0b101
> > + *  - BCFI shadow stack   0b110
> >*  - M mode HLV/HLVX/HSV 0b111
> >*/
> >   #define NB_MMU_MODES 8
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > index 6b4e90eb91..14cfb93288 100644
> > --- a/target/riscv/cpu.c
> > +++ b/target/riscv/cpu.c
> > @@ -584,6 +584,8 @@ static void riscv_cpu_reset_hold(Object *obj)
> >   }
> >   /* mmte is supposed to have pm.current hardwired to 1 */
> >   env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
> > +/* Initialize ss_priv to current priv. */
> > +env->ss_priv = env->priv;
> >   #endif
> >   env->xl = riscv_cpu_mxl(env);
> >   riscv_cpu_update_mask(env);
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index d14ea4f91d..8803ea6426 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -379,6 +379,7 @@ struct CPUArchState {
> >   uint64_t sstateen[SMSTATEEN_MAX_COUNT];
> >   target_ulong senvcfg;
> >   uint64_t henvcfg;
> > +target_ulong ss_priv;
> >   #endif
> >   target_ulong cur_pmmask;
> >   target_ulong cur_pmbase;
> > @@ -617,6 +618,8 @@ void riscv_cpu_set_fflags(CPURISCVState *env, 
> > target_ulong);
> >   #define TB_FLAGS_PRIV_HYP_ACCESS_MASK   (1 << 2)
> >   #define TB_FLAGS_MSTATUS_FS MSTATUS_FS
> >   #define TB_FLAGS_MSTATUS_VS MSTATUS_VS
> > +/* TLB MMU index for shadow stack accesses */
> > +#define MMU_IDX_SS_ACCESS6
> >
> >   #include "exec/cpu-all.h"
> >
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> > index fc188683c9..63377abc2f 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -657,7 +657,8 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, 
> > bool enable)
> >
> >   bool riscv_cpu_two_stage_lookup(int mmu_idx)
> >   {
> > -return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK;
> > +return (mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK) &&
> > +   (mmu_idx != MMU_IDX_SS_ACCESS);
> >   }
> >
> >   int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
> > @@ -745,6 +746,38 @@ void riscv_cpu_set_mode(CPURISCVState *env, 
> > target_ulong newpriv)
> >* preemptive context switch. As a result, do both.
> >*/
> >   env->load_res = -1;
> > +
> > +if (cpu_get_bcfien(env) && (env->priv != env->ss_priv)) {
> > +/*
> > + * If backward CFI is enabled in the new privilege state, the
> > + * shadow stack TLB needs to be flushed - unless the most recent
> > + * use of the SS TLB was for the same privilege mode.
> > + */
> > +tlb_flush_by_mmuidx(env_cpu(env), 1 << MMU_IDX_SS_ACCESS);
> > +/*
> > + * Ignoring env->virt here since currently every time it flips,
> > + * all TLBs are flushed anyway.
> > + */
> > +

Re: [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack

2023-02-15 Thread LIU Zhiwei



On 2023/2/9 14:24, Deepak Gupta wrote:

zisslpcfi protects returns(back cfi) using shadow stack. If compiled with
enabled compiler, function prologs will have `sspush ra` instruction to
push return address on shadow stack and function epilogs will have
`sspop t0; sschckra` instruction sequences. `sspop t0` will pop the
value from top of the shadow stack in t0. `sschckra` will compare `t0`
and `x1` and if they don't match then hart will raise an illegal
instruction exception.

Shadow stack is read-only memory except stores can be performed via
`sspush` and `ssamoswap` instructions. This requires new PTE encoding for
shadow stack. zisslpcfi uses R=0, W=1, X=0 (an existing reserved encoding
) to encode a shadow stack. If backward cfi is not enabled for current
mode, shadow stack PTE encodings remain reserved. Regular stores to
shadow stack raise AMO/store access fault. Shadow stack loads/stores on
regular memory raise load access/store access fault.

This patch creates a new MMU TLB index for shadow stack and flushes TLB
for shadow stack on privileges changes. This patch doesn't implement
`Smepmp` related enforcement on shadow stack pmp entry. Reason being qemu
doesn't have `Smepmp` implementation yet.

I don't know that the Smepmp means here. QEMU has supported the epmp.

  `Smepmp` enforcement should come
whenever it is implemented.

Signed-off-by: Deepak Gupta 
Signed-off-by: Kip Walker  
---
  target/riscv/cpu-param.h  |   1 +
  target/riscv/cpu.c|   2 +
  target/riscv/cpu.h|   3 ++
  target/riscv/cpu_helper.c | 107 +++---
  4 files changed, 94 insertions(+), 19 deletions(-)

diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h
index ebaf26d26d..a1e379beb7 100644
--- a/target/riscv/cpu-param.h
+++ b/target/riscv/cpu-param.h
@@ -25,6 +25,7 @@
   *  - M mode 0b011
   *  - U mode HLV/HLVX/HSV 0b100
   *  - S mode HLV/HLVX/HSV 0b101
+ *  - BCFI shadow stack   0b110
   *  - M mode HLV/HLVX/HSV 0b111
   */
  #define NB_MMU_MODES 8
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 6b4e90eb91..14cfb93288 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -584,6 +584,8 @@ static void riscv_cpu_reset_hold(Object *obj)
  }
  /* mmte is supposed to have pm.current hardwired to 1 */
  env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
+/* Initialize ss_priv to current priv. */
+env->ss_priv = env->priv;
  #endif
  env->xl = riscv_cpu_mxl(env);
  riscv_cpu_update_mask(env);
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index d14ea4f91d..8803ea6426 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -379,6 +379,7 @@ struct CPUArchState {
  uint64_t sstateen[SMSTATEEN_MAX_COUNT];
  target_ulong senvcfg;
  uint64_t henvcfg;
+target_ulong ss_priv;
  #endif
  target_ulong cur_pmmask;
  target_ulong cur_pmbase;
@@ -617,6 +618,8 @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
  #define TB_FLAGS_PRIV_HYP_ACCESS_MASK   (1 << 2)
  #define TB_FLAGS_MSTATUS_FS MSTATUS_FS
  #define TB_FLAGS_MSTATUS_VS MSTATUS_VS
+/* TLB MMU index for shadow stack accesses */
+#define MMU_IDX_SS_ACCESS6
  
  #include "exec/cpu-all.h"
  
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c

index fc188683c9..63377abc2f 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -657,7 +657,8 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool 
enable)
  
  bool riscv_cpu_two_stage_lookup(int mmu_idx)

  {
-return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK;
+return (mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK) &&
+   (mmu_idx != MMU_IDX_SS_ACCESS);
  }
  
  int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)

@@ -745,6 +746,38 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong 
newpriv)
   * preemptive context switch. As a result, do both.
   */
  env->load_res = -1;
+
+if (cpu_get_bcfien(env) && (env->priv != env->ss_priv)) {
+/*
+ * If backward CFI is enabled in the new privilege state, the
+ * shadow stack TLB needs to be flushed - unless the most recent
+ * use of the SS TLB was for the same privilege mode.
+ */
+tlb_flush_by_mmuidx(env_cpu(env), 1 << MMU_IDX_SS_ACCESS);
+/*
+ * Ignoring env->virt here since currently every time it flips,
+ * all TLBs are flushed anyway.
+ */
+env->ss_priv = env->priv;
+}
+}
+
+typedef enum {
+SSTACK_NO,  /* Access is not for a shadow stack instruction */
+SSTACK_YES, /* Access is for a shadow stack instruction */
+SSTACK_DC   /* Don't care about SS attribute in PMP */
+} SStackPmpMode;
+
+static bool legal_sstack_access(int access_type, bool sstack_inst,
+bool sstack_attribute)
+{
+/*
+ * Read/write/execution permissions are checked as usual. Shadow
+ * stack enforcement is just that (1) instruct

[PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack

2023-02-08 Thread Deepak Gupta
zisslpcfi protects returns(back cfi) using shadow stack. If compiled with
enabled compiler, function prologs will have `sspush ra` instruction to
push return address on shadow stack and function epilogs will have
`sspop t0; sschckra` instruction sequences. `sspop t0` will pop the
value from top of the shadow stack in t0. `sschckra` will compare `t0`
and `x1` and if they don't match then hart will raise an illegal
instruction exception.

Shadow stack is read-only memory except stores can be performed via
`sspush` and `ssamoswap` instructions. This requires new PTE encoding for
shadow stack. zisslpcfi uses R=0, W=1, X=0 (an existing reserved encoding
) to encode a shadow stack. If backward cfi is not enabled for current
mode, shadow stack PTE encodings remain reserved. Regular stores to
shadow stack raise AMO/store access fault. Shadow stack loads/stores on
regular memory raise load access/store access fault.

This patch creates a new MMU TLB index for shadow stack and flushes TLB
for shadow stack on privileges changes. This patch doesn't implement
`Smepmp` related enforcement on shadow stack pmp entry. Reason being qemu
doesn't have `Smepmp` implementation yet. `Smepmp` enforcement should come
whenever it is implemented.

Signed-off-by: Deepak Gupta 
Signed-off-by: Kip Walker  
---
 target/riscv/cpu-param.h  |   1 +
 target/riscv/cpu.c|   2 +
 target/riscv/cpu.h|   3 ++
 target/riscv/cpu_helper.c | 107 +++---
 4 files changed, 94 insertions(+), 19 deletions(-)

diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h
index ebaf26d26d..a1e379beb7 100644
--- a/target/riscv/cpu-param.h
+++ b/target/riscv/cpu-param.h
@@ -25,6 +25,7 @@
  *  - M mode 0b011
  *  - U mode HLV/HLVX/HSV 0b100
  *  - S mode HLV/HLVX/HSV 0b101
+ *  - BCFI shadow stack   0b110
  *  - M mode HLV/HLVX/HSV 0b111
  */
 #define NB_MMU_MODES 8
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 6b4e90eb91..14cfb93288 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -584,6 +584,8 @@ static void riscv_cpu_reset_hold(Object *obj)
 }
 /* mmte is supposed to have pm.current hardwired to 1 */
 env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
+/* Initialize ss_priv to current priv. */
+env->ss_priv = env->priv;
 #endif
 env->xl = riscv_cpu_mxl(env);
 riscv_cpu_update_mask(env);
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index d14ea4f91d..8803ea6426 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -379,6 +379,7 @@ struct CPUArchState {
 uint64_t sstateen[SMSTATEEN_MAX_COUNT];
 target_ulong senvcfg;
 uint64_t henvcfg;
+target_ulong ss_priv;
 #endif
 target_ulong cur_pmmask;
 target_ulong cur_pmbase;
@@ -617,6 +618,8 @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
 #define TB_FLAGS_PRIV_HYP_ACCESS_MASK   (1 << 2)
 #define TB_FLAGS_MSTATUS_FS MSTATUS_FS
 #define TB_FLAGS_MSTATUS_VS MSTATUS_VS
+/* TLB MMU index for shadow stack accesses */
+#define MMU_IDX_SS_ACCESS6
 
 #include "exec/cpu-all.h"
 
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index fc188683c9..63377abc2f 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -657,7 +657,8 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool 
enable)
 
 bool riscv_cpu_two_stage_lookup(int mmu_idx)
 {
-return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK;
+return (mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK) &&
+   (mmu_idx != MMU_IDX_SS_ACCESS);
 }
 
 int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
@@ -745,6 +746,38 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong 
newpriv)
  * preemptive context switch. As a result, do both.
  */
 env->load_res = -1;
+
+if (cpu_get_bcfien(env) && (env->priv != env->ss_priv)) {
+/*
+ * If backward CFI is enabled in the new privilege state, the
+ * shadow stack TLB needs to be flushed - unless the most recent
+ * use of the SS TLB was for the same privilege mode.
+ */
+tlb_flush_by_mmuidx(env_cpu(env), 1 << MMU_IDX_SS_ACCESS);
+/*
+ * Ignoring env->virt here since currently every time it flips,
+ * all TLBs are flushed anyway.
+ */
+env->ss_priv = env->priv;
+}
+}
+
+typedef enum {
+SSTACK_NO,  /* Access is not for a shadow stack instruction */
+SSTACK_YES, /* Access is for a shadow stack instruction */
+SSTACK_DC   /* Don't care about SS attribute in PMP */
+} SStackPmpMode;
+
+static bool legal_sstack_access(int access_type, bool sstack_inst,
+bool sstack_attribute)
+{
+/*
+ * Read/write/execution permissions are checked as usual. Shadow
+ * stack enforcement is just that (1) instruction type must match
+ * the attribute unless (2) a non-SS load to an SS region.
+ */
+return (sstack_inst == sstack_attribute) ||
+((acces

[PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack

2023-02-08 Thread Deepak Gupta
zisslpcfi protects returns(back cfi) using shadow stack. If compiled with
enabled compiler, function prologs will have `sspush ra` instruction to
push return address on shadow stack and function epilogs will have
`sspop t0; sschckra` instruction sequences. `sspop t0` will pop the
value from top of the shadow stack in t0. `sschckra` will compare `t0`
and `x1` and if they don't match then hart will raise an illegal
instruction exception.

Shadow stack is read-only memory except stores can be performed via
`sspush` and `ssamoswap` instructions. This requires new PTE encoding for
shadow stack. zisslpcfi uses R=0, W=1, X=0 (an existing reserved encoding
) to encode a shadow stack. If backward cfi is not enabled for current
mode, shadow stack PTE encodings remain reserved. Regular stores to
shadow stack raise AMO/store access fault. Shadow stack loads/stores on
regular memory raise load access/store access fault.

This patch creates a new MMU TLB index for shadow stack and flushes TLB
for shadow stack on privileges changes. This patch doesn't implement
`Smepmp` related enforcement on shadow stack pmp entry. Reason being qemu
doesn't have `Smepmp` implementation yet. `Smepmp` enforcement should come
whenever it is implemented.

Signed-off-by: Deepak Gupta 
Signed-off-by: Kip Walker  
---
 target/riscv/cpu-param.h  |   1 +
 target/riscv/cpu.c|   2 +
 target/riscv/cpu.h|   3 ++
 target/riscv/cpu_helper.c | 107 +++---
 4 files changed, 94 insertions(+), 19 deletions(-)

diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h
index ebaf26d26d..a1e379beb7 100644
--- a/target/riscv/cpu-param.h
+++ b/target/riscv/cpu-param.h
@@ -25,6 +25,7 @@
  *  - M mode 0b011
  *  - U mode HLV/HLVX/HSV 0b100
  *  - S mode HLV/HLVX/HSV 0b101
+ *  - BCFI shadow stack   0b110
  *  - M mode HLV/HLVX/HSV 0b111
  */
 #define NB_MMU_MODES 8
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 6b4e90eb91..14cfb93288 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -584,6 +584,8 @@ static void riscv_cpu_reset_hold(Object *obj)
 }
 /* mmte is supposed to have pm.current hardwired to 1 */
 env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
+/* Initialize ss_priv to current priv. */
+env->ss_priv = env->priv;
 #endif
 env->xl = riscv_cpu_mxl(env);
 riscv_cpu_update_mask(env);
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index d14ea4f91d..8803ea6426 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -379,6 +379,7 @@ struct CPUArchState {
 uint64_t sstateen[SMSTATEEN_MAX_COUNT];
 target_ulong senvcfg;
 uint64_t henvcfg;
+target_ulong ss_priv;
 #endif
 target_ulong cur_pmmask;
 target_ulong cur_pmbase;
@@ -617,6 +618,8 @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
 #define TB_FLAGS_PRIV_HYP_ACCESS_MASK   (1 << 2)
 #define TB_FLAGS_MSTATUS_FS MSTATUS_FS
 #define TB_FLAGS_MSTATUS_VS MSTATUS_VS
+/* TLB MMU index for shadow stack accesses */
+#define MMU_IDX_SS_ACCESS6
 
 #include "exec/cpu-all.h"
 
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index fc188683c9..63377abc2f 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -657,7 +657,8 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool 
enable)
 
 bool riscv_cpu_two_stage_lookup(int mmu_idx)
 {
-return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK;
+return (mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK) &&
+   (mmu_idx != MMU_IDX_SS_ACCESS);
 }
 
 int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
@@ -745,6 +746,38 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong 
newpriv)
  * preemptive context switch. As a result, do both.
  */
 env->load_res = -1;
+
+if (cpu_get_bcfien(env) && (env->priv != env->ss_priv)) {
+/*
+ * If backward CFI is enabled in the new privilege state, the
+ * shadow stack TLB needs to be flushed - unless the most recent
+ * use of the SS TLB was for the same privilege mode.
+ */
+tlb_flush_by_mmuidx(env_cpu(env), 1 << MMU_IDX_SS_ACCESS);
+/*
+ * Ignoring env->virt here since currently every time it flips,
+ * all TLBs are flushed anyway.
+ */
+env->ss_priv = env->priv;
+}
+}
+
+typedef enum {
+SSTACK_NO,  /* Access is not for a shadow stack instruction */
+SSTACK_YES, /* Access is for a shadow stack instruction */
+SSTACK_DC   /* Don't care about SS attribute in PMP */
+} SStackPmpMode;
+
+static bool legal_sstack_access(int access_type, bool sstack_inst,
+bool sstack_attribute)
+{
+/*
+ * Read/write/execution permissions are checked as usual. Shadow
+ * stack enforcement is just that (1) instruction type must match
+ * the attribute unless (2) a non-SS load to an SS region.
+ */
+return (sstack_inst == sstack_attribute) ||
+((acces