Re: [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack
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
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
`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
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
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
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
