[PATCH v15 14/21] target/riscv: disallow probe accesses to shadow stack

Deepak Gupta posted 21 patches 1 month, 3 weeks ago
There is a newer version of this series
[PATCH v15 14/21] target/riscv: disallow probe accesses to shadow stack
Posted by Deepak Gupta 1 month, 3 weeks ago
`check_zicbom_access` (`cbo.clean/flush/inval`) may probe shadow stack
memory and must always raise store/AMO access fault because it has store
semantics.

For non-shadow stack memory even though `cbo.clean/flush/inval` have
store semantics, it will not fault if read is allowed (probably to follow
`clflush` on x86). Although if read is not allowed, eventually
`probe_write` will do store page (or access) fault (if permissions don't
allow it).

cbo operations on shadow stack memory must always raise store access fault.
Thus extending `get_physical_address` to recieve `probe` parameter as well.

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
---
 target/riscv/cpu_helper.c | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 5580f5f3f3..ab46f694b5 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -884,7 +884,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
                                 target_ulong *fault_pte_addr,
                                 int access_type, int mmu_idx,
                                 bool first_stage, bool two_stage,
-                                bool is_debug)
+                                bool is_debug, bool is_probe)
 {
     /*
      * NOTE: the env->pc value visible here will not be
@@ -1030,7 +1030,7 @@ restart:
             int vbase_ret = get_physical_address(env, &vbase, &vbase_prot,
                                                  base, NULL, MMU_DATA_LOAD,
                                                  MMUIdx_U, false, true,
-                                                 is_debug);
+                                                 is_debug, false);
 
             if (vbase_ret != TRANSLATE_SUCCESS) {
                 if (fault_pte_addr) {
@@ -1117,8 +1117,11 @@ restart:
         /* if bcfi enabled, PTE_W is not reserved and shadow stack page */
         if (cpu_get_bcfien(env) && first_stage) {
             sstack_page = true;
-            /* if ss index, read and write allowed. else only read allowed */
-            rwx = is_sstack_idx ? PTE_R | PTE_W : PTE_R;
+            /*
+             * if ss index, read and write allowed. else if not a probe
+             * then only read allowed
+             */
+            rwx = is_sstack_idx ? (PTE_R | PTE_W) : (is_probe ? rwx :  PTE_R);
             break;
         }
         return TRANSLATE_FAIL;
@@ -1327,13 +1330,13 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
     int mmu_idx = riscv_env_mmu_index(&cpu->env, false);
 
     if (get_physical_address(env, &phys_addr, &prot, addr, NULL, 0, mmu_idx,
-                             true, env->virt_enabled, true)) {
+                             true, env->virt_enabled, true, false)) {
         return -1;
     }
 
     if (env->virt_enabled) {
         if (get_physical_address(env, &phys_addr, &prot, phys_addr, NULL,
-                                 0, MMUIdx_U, false, true, true)) {
+                                 0, MMUIdx_U, false, true, true, false)) {
             return -1;
         }
     }
@@ -1447,7 +1450,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
         /* Two stage lookup */
         ret = get_physical_address(env, &pa, &prot, address,
                                    &env->guest_phys_fault_addr, access_type,
-                                   mmu_idx, true, true, false);
+                                   mmu_idx, true, true, false, probe);
 
         /*
          * A G-stage exception may be triggered during two state lookup.
@@ -1470,7 +1473,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
 
             ret = get_physical_address(env, &pa, &prot2, im_address, NULL,
                                        access_type, MMUIdx_U, false, true,
-                                       false);
+                                       false, probe);
 
             qemu_log_mask(CPU_LOG_MMU,
                           "%s 2nd-stage address=%" VADDR_PRIx
@@ -1507,7 +1510,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
     } else {
         /* Single stage lookup */
         ret = get_physical_address(env, &pa, &prot, address, NULL,
-                                   access_type, mmu_idx, true, false, false);
+                                   access_type, mmu_idx, true, false, false,
+                                   probe);
 
         qemu_log_mask(CPU_LOG_MMU,
                       "%s address=%" VADDR_PRIx " ret %d physical "
-- 
2.45.0
Re: [PATCH v15 14/21] target/riscv: disallow probe accesses to shadow stack
Posted by Deepak Gupta 1 month, 2 weeks ago
On Thu, Oct 03, 2024 at 11:33:35AM -0700, Deepak Gupta wrote:
>`check_zicbom_access` (`cbo.clean/flush/inval`) may probe shadow stack
>memory and must always raise store/AMO access fault because it has store
>semantics.
>
>For non-shadow stack memory even though `cbo.clean/flush/inval` have
>store semantics, it will not fault if read is allowed (probably to follow
>`clflush` on x86). Although if read is not allowed, eventually
>`probe_write` will do store page (or access) fault (if permissions don't
>allow it).
>
>cbo operations on shadow stack memory must always raise store access fault.
>Thus extending `get_physical_address` to recieve `probe` parameter as well.
>
>Signed-off-by: Deepak Gupta <debug@rivosinc.com>
>---
> target/riscv/cpu_helper.c | 22 +++++++++++++---------
> 1 file changed, 13 insertions(+), 9 deletions(-)
>
>diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
>index 5580f5f3f3..ab46f694b5 100644
>--- a/target/riscv/cpu_helper.c
>+++ b/target/riscv/cpu_helper.c
>@@ -884,7 +884,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
>                                 target_ulong *fault_pte_addr,
>                                 int access_type, int mmu_idx,
>                                 bool first_stage, bool two_stage,
>-                                bool is_debug)
>+                                bool is_debug, bool is_probe)
> {
>     /*
>      * NOTE: the env->pc value visible here will not be
>@@ -1030,7 +1030,7 @@ restart:
>             int vbase_ret = get_physical_address(env, &vbase, &vbase_prot,
>                                                  base, NULL, MMU_DATA_LOAD,
>                                                  MMUIdx_U, false, true,
>-                                                 is_debug);
>+                                                 is_debug, false);
>
>             if (vbase_ret != TRANSLATE_SUCCESS) {
>                 if (fault_pte_addr) {
>@@ -1117,8 +1117,11 @@ restart:
>         /* if bcfi enabled, PTE_W is not reserved and shadow stack page */
>         if (cpu_get_bcfien(env) && first_stage) {
>             sstack_page = true;
>-            /* if ss index, read and write allowed. else only read allowed */
>-            rwx = is_sstack_idx ? PTE_R | PTE_W : PTE_R;
>+            /*
>+             * if ss index, read and write allowed. else if not a probe
>+             * then only read allowed
>+             */
>+            rwx = is_sstack_idx ? (PTE_R | PTE_W) : (is_probe ? rwx :  PTE_R);

hmm... there is a bug here. Above would allow writes to go through if probed.

It should be 


               rwx = is_sstack_idx ? (PTE_R | PTE_W) : (is_probe ? 0 :  PTE_R);

I'll fix it in next version.
Still need higher level feedback on patch that if it's okay to extend
`get_physical_address` to recieve `probe` parameter.


>             break;
>         }
>         return TRANSLATE_FAIL;
>@@ -1327,13 +1330,13 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
>     int mmu_idx = riscv_env_mmu_index(&cpu->env, false);
>
>     if (get_physical_address(env, &phys_addr, &prot, addr, NULL, 0, mmu_idx,
>-                             true, env->virt_enabled, true)) {
>+                             true, env->virt_enabled, true, false)) {
>         return -1;
>     }
>
>     if (env->virt_enabled) {
>         if (get_physical_address(env, &phys_addr, &prot, phys_addr, NULL,
>-                                 0, MMUIdx_U, false, true, true)) {
>+                                 0, MMUIdx_U, false, true, true, false)) {
>             return -1;
>         }
>     }
>@@ -1447,7 +1450,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>         /* Two stage lookup */
>         ret = get_physical_address(env, &pa, &prot, address,
>                                    &env->guest_phys_fault_addr, access_type,
>-                                   mmu_idx, true, true, false);
>+                                   mmu_idx, true, true, false, probe);
>
>         /*
>          * A G-stage exception may be triggered during two state lookup.
>@@ -1470,7 +1473,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>
>             ret = get_physical_address(env, &pa, &prot2, im_address, NULL,
>                                        access_type, MMUIdx_U, false, true,
>-                                       false);
>+                                       false, probe);
>
>             qemu_log_mask(CPU_LOG_MMU,
>                           "%s 2nd-stage address=%" VADDR_PRIx
>@@ -1507,7 +1510,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>     } else {
>         /* Single stage lookup */
>         ret = get_physical_address(env, &pa, &prot, address, NULL,
>-                                   access_type, mmu_idx, true, false, false);
>+                                   access_type, mmu_idx, true, false, false,
>+                                   probe);
>
>         qemu_log_mask(CPU_LOG_MMU,
>                       "%s address=%" VADDR_PRIx " ret %d physical "
>-- 
>2.45.0
>