diff mbox series

[for,5.0,v1,1/2] riscv: Don't use stage-2 PTE lookup protection flags

Message ID 931db85d6890ed4bc2b527fd1011197cd28299aa.1585262586.git.alistair.francis@wdc.com (mailing list archive)
State New, archived
Headers show
Series RISC-V: Fix Hypervisor guest user space | expand

Commit Message

Alistair Francis March 26, 2020, 10:44 p.m. UTC
When doing the fist of a two stage lookup (Hypervisor extensions) don't
set the current protection flags from the second stage lookup of the
base address PTE.

Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
---
 target/riscv/cpu_helper.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

Comments

Richard Henderson March 26, 2020, 11:50 p.m. UTC | #1
On 3/26/20 3:44 PM, Alistair Francis wrote:
> When doing the fist of a two stage lookup (Hypervisor extensions) don't
> set the current protection flags from the second stage lookup of the
> base address PTE.
> 
> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
> ---
>  target/riscv/cpu_helper.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index d3ba9efb02..f36d184b7b 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -452,10 +452,11 @@ restart:
>          hwaddr pte_addr;
>  
>          if (two_stage && first_stage) {
> +            int vbase_prot;
>              hwaddr vbase;
>  
>              /* Do the second stage translation on the base PTE address. */
> -            get_physical_address(env, &vbase, prot, base, access_type,
> +            get_physical_address(env, &vbase, &vbase_prot, base, access_type,
>                                   mmu_idx, false, true);
>  
>              pte_addr = vbase + idx * ptesize;
> 

Certainly stage2 pte lookup has nothing to do with the original lookup, so
using a new variable for prot is correct.

So as far as this minimal patch,

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

However, this bit of code doesn't look right:

(1) Similarly, what has the original access_type got to do with the PTE lookup?
 Seems like this should be MMU_DATA_LOAD always.

(2) Why is the get_physical_address return value ignored?  On failure, surely
this should be some sort of PTE lookup failure.

(3) Do we need to validate vbase_prot for write before updating the PTE for
Access or Dirty?  That seems like a loop-hole to allow silent modification of
hypervisor read-only memory.

I do wonder if it might be easier to manage all of this by using additional
TLBs to handle the stage2 and physical address spaces.  That's probably too
invasive for this stage of development though.


r~
Alistair Francis June 25, 2020, 7:02 p.m. UTC | #2
On Thu, Mar 26, 2020 at 4:50 PM Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 3/26/20 3:44 PM, Alistair Francis wrote:
> > When doing the fist of a two stage lookup (Hypervisor extensions) don't
> > set the current protection flags from the second stage lookup of the
> > base address PTE.
> >
> > Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
> > ---
> >  target/riscv/cpu_helper.c | 3 ++-
> >  1 file changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> > index d3ba9efb02..f36d184b7b 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -452,10 +452,11 @@ restart:
> >          hwaddr pte_addr;
> >
> >          if (two_stage && first_stage) {
> > +            int vbase_prot;
> >              hwaddr vbase;
> >
> >              /* Do the second stage translation on the base PTE address. */
> > -            get_physical_address(env, &vbase, prot, base, access_type,
> > +            get_physical_address(env, &vbase, &vbase_prot, base, access_type,
> >                                   mmu_idx, false, true);
> >
> >              pte_addr = vbase + idx * ptesize;
> >
>
> Certainly stage2 pte lookup has nothing to do with the original lookup, so
> using a new variable for prot is correct.
>
> So as far as this minimal patch,
>
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
>
> However, this bit of code doesn't look right:

Thanks for the comments here. Coming back to this after a while.

>
> (1) Similarly, what has the original access_type got to do with the PTE lookup?
>  Seems like this should be MMU_DATA_LOAD always.

Fixed in master now

>
> (2) Why is the get_physical_address return value ignored?  On failure, surely
> this should be some sort of PTE lookup failure.

Also fixed in master now

>
> (3) Do we need to validate vbase_prot for write before updating the PTE for
> Access or Dirty?  That seems like a loop-hole to allow silent modification of
> hypervisor read-only memory.

That's a good point.

Updating the accessed bit seems correct to me as we did access it and
that doesn't then provide write permissions.

Updating the dirty bit would provide write permissions, but we would
only change the dirty bit on a store and vbase_prot is now always a
load.

If the PTE was already dirty then we might incorrectly provide write
access though.

>
> I do wonder if it might be easier to manage all of this by using additional
> TLBs to handle the stage2 and physical address spaces.  That's probably too
> invasive for this stage of development though.

Do you mean change riscv_cpu_mmu_index() to take into account
virtulisation and have more then the current 3 (M, S and U) MMU
indexes?

Alistair

>
>
> r~
Richard Henderson June 27, 2020, 10:48 p.m. UTC | #3
On 6/25/20 12:02 PM, Alistair Francis wrote:
>> (3) Do we need to validate vbase_prot for write before updating the PTE for
>> Access or Dirty?  That seems like a loop-hole to allow silent modification of
>> hypervisor read-only memory.
> 
> That's a good point.
> 
> Updating the accessed bit seems correct to me as we did access it and
> that doesn't then provide write permissions.

I guess my first question is: Does the stage2 hypervisor pte provide read-only
memory?

If not, all of this is moot.

However, if it does, consider:

  (1) The guest os creates a stage1 page table with a leaf table
      within the read-only memory.  This is obviously hokey.

  (2) The guest os accesses a virtual address that utilizes the
      aforementioned PTE, the hardware (qemu) updates the
      accessed bit.

  (3) The read-only page has now been modified.  Oops.

>> I do wonder if it might be easier to manage all of this by using additional
>> TLBs to handle the stage2 and physical address spaces.  That's probably too
>> invasive for this stage of development though.
> 
> Do you mean change riscv_cpu_mmu_index() to take into account
> virtulisation and have more then the current 3 (M, S and U) MMU
> indexes?

I had been thinking that you might be able to use some form of mmu-indexed
load/lookup instead of address_space_ldq.  Which would require 1 mmuidx that is
physically mapped (same as M?) and another that uses only the hypervisor's
second stage lookup.


r~
diff mbox series

Patch

diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index d3ba9efb02..f36d184b7b 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -452,10 +452,11 @@  restart:
         hwaddr pte_addr;
 
         if (two_stage && first_stage) {
+            int vbase_prot;
             hwaddr vbase;
 
             /* Do the second stage translation on the base PTE address. */
-            get_physical_address(env, &vbase, prot, base, access_type,
+            get_physical_address(env, &vbase, &vbase_prot, base, access_type,
                                  mmu_idx, false, true);
 
             pte_addr = vbase + idx * ptesize;