Re: [RFC PATCH v4 1/2] ARM: KVM: one_reg coproc set and get BE fixes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, May 28, 2014 at 08:38:33AM -0700, Victor Kamensky wrote:
> On 28 May 2014 02:55, Christoffer Dall <christoffer.dall@xxxxxxxxxx> wrote:
> > On Tue, May 27, 2014 at 11:08:11PM -0700, Victor Kamensky wrote:
> >> Fix code that handles KVM_SET_ONE_REG, KVM_GET_ONE_REG ioctls to work in BE
> >> image. Before this fix get/set_one_reg functions worked correctly only in
> >> LE case - reg_from_user was taking 'void *' kernel address that actually could
> >> be target/source memory of either 4 bytes size or 8 bytes size, and code copied
> >> from/to user memory that could hold either 4 bytes register, 8 byte register
> >> or pair of 4 bytes registers.
> >>
> >> For example note that there was a case when 4 bytes register was read from
> >> userspace to kernel target address of 8 bytes value. Because it was working
> >> in LE, least significant word was memcpy(ied) and it just worked. In BE code
> >> with 'void *' as target/source 'val' type it is impossible to tell whether
> >> 4 bytes register from userspace should be copied to 'val' address itself
> >> (4 bytes target) or it should be copied to 'val' + 4 (least significant word
> >> of 8 bytes value). So first change was to introduce strongly typed
> >> functions, where type of target/source 'val' is strongly defined:
> >>
> >> reg_from_user_to_u64 - reads register from userspace to kernel 'u64 *val'
> >>                   address; register size must be 8 bytes
> >> reg_from_user_to_u32 - reads register(s) from userspace to kernel 'u32 *val'
> >>                   address; note it could be one or two 4 bytes registers
> >> reg_to_user_from_u64 - writes register from kernel 'u64 *val' address to
> >>                   userspace register memory; register size must be 8 bytes
> >> ret_to_user_from_u32 - writes register(s) from kernel 'u32 *val' address to
> >>                   userspace register(s) memory; note it could be
> >>                   one or two 4 bytes registers
> >>
> >> All places where reg_from_user, reg_to_user functions were used, were changed
> >> to use either corresponding u64 or u32 variants of functions depending on
> >> type of source/target kernel memory variable.
> >>
> >> Signed-off-by: Victor Kamensky <victor.kamensky@xxxxxxxxxx>
> >> ---
> >>  arch/arm/kvm/coproc.c | 111 ++++++++++++++++++++++++++++++++++++++------------
> >>  1 file changed, 84 insertions(+), 27 deletions(-)
> >>
> >> diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
> >> index c58a351..84d3ddb 100644
> >> --- a/arch/arm/kvm/coproc.c
> >> +++ b/arch/arm/kvm/coproc.c
> >> @@ -682,17 +682,60 @@ static struct coproc_reg invariant_cp15[] = {
> >>       { CRn( 0), CRm( 0), Op1( 1), Op2( 7), is32, NULL, get_AIDR },
> >>  };
> >>
> >> -static int reg_from_user(void *val, const void __user *uaddr, u64 id)
> >> +/*
> >> + * Reads a register value from a userspace address to a kernel u64
> >> + * variable. Register size must be always 8 bytes.
> >> + */
> >> +static int reg_from_user_to_u64(u64 *val, const void __user *uaddr, u64 id)
> >> +{
> >> +     unsigned long regsize = KVM_REG_SIZE(id);
> >> +
> >> +     if (regsize != 8)
> >> +              return -ENOENT;
> >
> > no such file?
> 
> What error code would you like me to use? I used ENOENT driven
> by existing examples in the same file like in
> 
> static int demux_c15_set(u64 id, void __user *uaddr)
> ...
>         if (KVM_REG_SIZE(id) != 4)
>             return -ENOENT;
> ...
> static int vfp_get_reg(const struct kvm_vcpu *vcpu, u64 id, void __user *uaddr)
> {
> ...
>         if (KVM_REG_SIZE(id) != 8)
>             return -ENOENT;
> 

hmm, maybe, I think those made more sence, because those were lookups
and didn't find any entry, but here it's more like someone just gave you
a wrong value to a particular function, so -EINVAL seemed more
appropriate.  But I'm probably over-nit-picking.

> > This error check actually makes me even more convinced that we're
> > pushing the wrong functionality into this function, the _u64 function
> > should only be called if we have regsize == 8.
> 
> Is not above check trying to enforce exactly what you said? _u64
> function will fail if it gets any other size than 8.

We shouldn't be writing defensive code in the kernel, we should call the
right functions.

> 
> Not on the side I will try to rework _u32 and regsize == 8 along the
> lines you suggested: read into u64 using _u64 version, and than use
> something like vcpu_cp15_64_high, vcpu_cp15_64_low to put it
> into array of u32. In this case I will add
> 
>         if (KVM_REG_SIZE(id) != 4)
>             return -ENOENT;
> 
> to make sure _32 version used only with regsize == 32
> 
> >
> >> +
> >> +     if (copy_from_user(val, uaddr, regsize) != 0)
> >> +             return -EFAULT;
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +/*
> >> + * Reads a register value from a userspace address to a kernel u32 variable
> >> + * or a kernel array of two u32 values. That is, it may really copy two
> >> + * u32 registers when used with registers defined by the ABI to be 8 bytes
> >> + * long (e.g. TTBR0/TTBR1).
> >> + */
> >> +static int reg_from_user_to_u32(u32 *val, const void __user *uaddr, u64 id)
> >>  {
> >> -     /* This Just Works because we are little endian. */
> >>       if (copy_from_user(val, uaddr, KVM_REG_SIZE(id)) != 0)
> >>               return -EFAULT;
> >>       return 0;
> >>  }
> >>
> >> -static int reg_to_user(void __user *uaddr, const void *val, u64 id)
> >> +/*
> >> + * Writes a register value to a userspace address from a kernel u64 value.
> >> + * Register size must be always 8 bytes.
> >> + */
> >> +static int reg_to_user_from_u64(void __user *uaddr, const u64 *val, u64 id)
> >> +{
> >> +     unsigned long regsize = KVM_REG_SIZE(id);
> >> +
> >> +     if (regsize != 8)
> >> +              return -ENOENT;
> >
> > same here
> >
> >> +
> >> +     if (copy_to_user(uaddr, val, regsize) != 0)
> >> +             return -EFAULT;
> >> +     return 0;
> >> +}
> >
> > so yeah, we may not need multiple functions at all...
> 
> I am not sure that I follow above completely, I think we would
> need to have separate _u32 and _u64 functions with proper
> checks inside to enforce rule that _u32 and kernel target
> type u32 is used with regsize == 4, and _u64 and kernel target
> type u64 is used with regsize == 8. Otherwise if it is only one
> function and it is called currently with the way we want, how
> can we make sure that in future these rules will stay the same?
> 
> Or maybe we can use one macro that checks the passed
> target kernel type size matches regsize. In this case code will look
> nice and we would have enforcement we need. Let me try
> that.
> 
Sounds good!
-Christoffer
_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm




[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux