diff mbox

x86/hvm/viridian: zero and check vcpu context __pad field

Message ID 1459333920-2182-1-git-send-email-paul.durrant@citrix.com (mailing list archive)
State New, archived
Headers show

Commit Message

Paul Durrant March 30, 2016, 10:32 a.m. UTC
Commit 57844631 "save APIC assist vector" added an extra field to the
viridian vcpu context save record. This field was only a uint8_t and
so an extra __pad field was also added to pad up to the next 64-bit
boundary.

This patch makes sure that __pad field is zeroed on save and checked
for zero on restore. This prevents a potential leak of information
from the stack and a compatibility check against future use of the
space occupied by the __pad field.

Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
Cc: Keir Fraser <keir@xen.org>
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
---
 xen/arch/x86/hvm/viridian.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

Comments

Jan Beulich March 30, 2016, 11:22 a.m. UTC | #1
>>> On 30.03.16 at 12:32, <paul.durrant@citrix.com> wrote:
> --- a/xen/arch/x86/hvm/viridian.c
> +++ b/xen/arch/x86/hvm/viridian.c
> @@ -824,6 +824,8 @@ static int viridian_save_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h)
>      for_each_vcpu( d, v ) {
>          struct hvm_viridian_vcpu_context ctxt;
>  
> +        memset(&ctxt, 0, sizeof(ctxt));

How about just adding an empty initializer to the declaration?

> @@ -834,6 +836,15 @@ static int viridian_save_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h)
>      return 0;
>  }
>  
> +static bool_t is_zero(void *p, size_t size)

At the very least this wants to be a pointer to const.

> +{
> +    while ( size-- )
> +        if ( *(uint8_t *)p++ != 0 )
> +            return 0;
> +
> +    return 1;
> +}
> +
>  static int viridian_load_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h)
>  {
>      int vcpuid;
> @@ -851,6 +862,9 @@ static int viridian_load_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h)
>      if ( hvm_load_entry_zeroextend(VIRIDIAN_VCPU, h, &ctxt) != 0 )
>          return -EINVAL;
>  
> +    if ( !is_zero(&ctxt._pad, sizeof(ctxt._pad)) )
> +        return -EINVAL;

"memcmp(&ctx._pad, zero_page, sizeof(ctxt._pad))" would be an
alternative not requiring any new helper function.

Jan
Paul Durrant March 30, 2016, 11:26 a.m. UTC | #2
> -----Original Message-----
> From: Jan Beulich [mailto:JBeulich@suse.com]
> Sent: 30 March 2016 12:23
> To: Paul Durrant
> Cc: Andrew Cooper; xen-devel@lists.xenproject.org; Keir (Xen.org)
> Subject: Re: [PATCH] x86/hvm/viridian: zero and check vcpu context __pad
> field
> 
> >>> On 30.03.16 at 12:32, <paul.durrant@citrix.com> wrote:
> > --- a/xen/arch/x86/hvm/viridian.c
> > +++ b/xen/arch/x86/hvm/viridian.c
> > @@ -824,6 +824,8 @@ static int viridian_save_vcpu_ctxt(struct domain *d,
> hvm_domain_context_t *h)
> >      for_each_vcpu( d, v ) {
> >          struct hvm_viridian_vcpu_context ctxt;
> >
> > +        memset(&ctxt, 0, sizeof(ctxt));
> 
> How about just adding an empty initializer to the declaration?
> 

I think having a 'zero the entire struct' call at the start is better as it will cover any additions made to the struct in future. It's what I had mistakenly assumed was already there. In fact I think adding a similar call into the domain context save function would probably be worthwhile.

> > @@ -834,6 +836,15 @@ static int viridian_save_vcpu_ctxt(struct domain
> *d, hvm_domain_context_t *h)
> >      return 0;
> >  }
> >
> > +static bool_t is_zero(void *p, size_t size)
> 
> At the very least this wants to be a pointer to const.
> 
> > +{
> > +    while ( size-- )
> > +        if ( *(uint8_t *)p++ != 0 )
> > +            return 0;
> > +
> > +    return 1;
> > +}
> > +
> >  static int viridian_load_vcpu_ctxt(struct domain *d,
> hvm_domain_context_t *h)
> >  {
> >      int vcpuid;
> > @@ -851,6 +862,9 @@ static int viridian_load_vcpu_ctxt(struct domain *d,
> hvm_domain_context_t *h)
> >      if ( hvm_load_entry_zeroextend(VIRIDIAN_VCPU, h, &ctxt) != 0 )
> >          return -EINVAL;
> >
> > +    if ( !is_zero(&ctxt._pad, sizeof(ctxt._pad)) )
> > +        return -EINVAL;
> 
> "memcmp(&ctx._pad, zero_page, sizeof(ctxt._pad))" would be an
> alternative not requiring any new helper function.
> 

Ah, I didn't know about the zero_page definition. I'll use that and drop the helper.

  Paul

> Jan
Jan Beulich March 30, 2016, 1:16 p.m. UTC | #3
>>> On 30.03.16 at 13:26, <Paul.Durrant@citrix.com> wrote:
>> From: Jan Beulich [mailto:JBeulich@suse.com]
>> Sent: 30 March 2016 12:23
>> >>> On 30.03.16 at 12:32, <paul.durrant@citrix.com> wrote:
>> > --- a/xen/arch/x86/hvm/viridian.c
>> > +++ b/xen/arch/x86/hvm/viridian.c
>> > @@ -824,6 +824,8 @@ static int viridian_save_vcpu_ctxt(struct domain *d,
>> hvm_domain_context_t *h)
>> >      for_each_vcpu( d, v ) {
>> >          struct hvm_viridian_vcpu_context ctxt;
>> >
>> > +        memset(&ctxt, 0, sizeof(ctxt));
>> 
>> How about just adding an empty initializer to the declaration?
>> 
> 
> I think having a 'zero the entire struct' call at the start is better as it 
> will cover any additions made to the struct in future. It's what I had 
> mistakenly assumed was already there. In fact I think adding a similar call 
> into the domain context save function would probably be worthwhile.

And how does the initializer approach not fulfill that intention?

Jan
Paul Durrant March 30, 2016, 1:19 p.m. UTC | #4
> -----Original Message-----
> From: Jan Beulich [mailto:JBeulich@suse.com]
> Sent: 30 March 2016 14:17
> To: Paul Durrant
> Cc: Andrew Cooper; xen-devel@lists.xenproject.org; Keir (Xen.org)
> Subject: RE: [PATCH] x86/hvm/viridian: zero and check vcpu context __pad
> field
> 
> >>> On 30.03.16 at 13:26, <Paul.Durrant@citrix.com> wrote:
> >> From: Jan Beulich [mailto:JBeulich@suse.com]
> >> Sent: 30 March 2016 12:23
> >> >>> On 30.03.16 at 12:32, <paul.durrant@citrix.com> wrote:
> >> > --- a/xen/arch/x86/hvm/viridian.c
> >> > +++ b/xen/arch/x86/hvm/viridian.c
> >> > @@ -824,6 +824,8 @@ static int viridian_save_vcpu_ctxt(struct domain
> *d,
> >> hvm_domain_context_t *h)
> >> >      for_each_vcpu( d, v ) {
> >> >          struct hvm_viridian_vcpu_context ctxt;
> >> >
> >> > +        memset(&ctxt, 0, sizeof(ctxt));
> >>
> >> How about just adding an empty initializer to the declaration?
> >>
> >
> > I think having a 'zero the entire struct' call at the start is better as it
> > will cover any additions made to the struct in future. It's what I had
> > mistakenly assumed was already there. In fact I think adding a similar call
> > into the domain context save function would probably be worthwhile.
> 
> And how does the initializer approach not fulfill that intention?
> 

Because any time anyone adds another field they have to remember to add another initializer, which is what I forgot to do. This approach OTOH is failsafe.

  Paul

> Jan
Jan Beulich March 30, 2016, 2:22 p.m. UTC | #5
>>> On 30.03.16 at 15:19, <Paul.Durrant@citrix.com> wrote:
>>  -----Original Message-----
>> From: Jan Beulich [mailto:JBeulich@suse.com]
>> Sent: 30 March 2016 14:17
>> To: Paul Durrant
>> Cc: Andrew Cooper; xen-devel@lists.xenproject.org; Keir (Xen.org)
>> Subject: RE: [PATCH] x86/hvm/viridian: zero and check vcpu context __pad
>> field
>> 
>> >>> On 30.03.16 at 13:26, <Paul.Durrant@citrix.com> wrote:
>> >> From: Jan Beulich [mailto:JBeulich@suse.com]
>> >> Sent: 30 March 2016 12:23
>> >> >>> On 30.03.16 at 12:32, <paul.durrant@citrix.com> wrote:
>> >> > --- a/xen/arch/x86/hvm/viridian.c
>> >> > +++ b/xen/arch/x86/hvm/viridian.c
>> >> > @@ -824,6 +824,8 @@ static int viridian_save_vcpu_ctxt(struct domain
>> *d,
>> >> hvm_domain_context_t *h)
>> >> >      for_each_vcpu( d, v ) {
>> >> >          struct hvm_viridian_vcpu_context ctxt;
>> >> >
>> >> > +        memset(&ctxt, 0, sizeof(ctxt));
>> >>
>> >> How about just adding an empty initializer to the declaration?
>> >>
>> >
>> > I think having a 'zero the entire struct' call at the start is better as it
>> > will cover any additions made to the struct in future. It's what I had
>> > mistakenly assumed was already there. In fact I think adding a similar call
>> > into the domain context save function would probably be worthwhile.
>> 
>> And how does the initializer approach not fulfill that intention?
>> 
> 
> Because any time anyone adds another field they have to remember to add 
> another initializer, which is what I forgot to do. This approach OTOH is 
> failsafe.

But note how I said "an empty initializer": When there is an
initializer at all, all fields not mentioned in the initializer will get
default initialized (i.e. zeroed). Hence an empty initializer
clears the entire structure.

Jan
Paul Durrant March 30, 2016, 3:16 p.m. UTC | #6
> -----Original Message-----
> From: Jan Beulich [mailto:JBeulich@suse.com]
> Sent: 30 March 2016 15:22
> To: Paul Durrant
> Cc: Andrew Cooper; xen-devel@lists.xenproject.org; Keir (Xen.org)
> Subject: RE: [PATCH] x86/hvm/viridian: zero and check vcpu context __pad
> field
> 
> >>> On 30.03.16 at 15:19, <Paul.Durrant@citrix.com> wrote:
> >>  -----Original Message-----
> >> From: Jan Beulich [mailto:JBeulich@suse.com]
> >> Sent: 30 March 2016 14:17
> >> To: Paul Durrant
> >> Cc: Andrew Cooper; xen-devel@lists.xenproject.org; Keir (Xen.org)
> >> Subject: RE: [PATCH] x86/hvm/viridian: zero and check vcpu context
> __pad
> >> field
> >>
> >> >>> On 30.03.16 at 13:26, <Paul.Durrant@citrix.com> wrote:
> >> >> From: Jan Beulich [mailto:JBeulich@suse.com]
> >> >> Sent: 30 March 2016 12:23
> >> >> >>> On 30.03.16 at 12:32, <paul.durrant@citrix.com> wrote:
> >> >> > --- a/xen/arch/x86/hvm/viridian.c
> >> >> > +++ b/xen/arch/x86/hvm/viridian.c
> >> >> > @@ -824,6 +824,8 @@ static int viridian_save_vcpu_ctxt(struct
> domain
> >> *d,
> >> >> hvm_domain_context_t *h)
> >> >> >      for_each_vcpu( d, v ) {
> >> >> >          struct hvm_viridian_vcpu_context ctxt;
> >> >> >
> >> >> > +        memset(&ctxt, 0, sizeof(ctxt));
> >> >>
> >> >> How about just adding an empty initializer to the declaration?
> >> >>
> >> >
> >> > I think having a 'zero the entire struct' call at the start is better as it
> >> > will cover any additions made to the struct in future. It's what I had
> >> > mistakenly assumed was already there. In fact I think adding a similar call
> >> > into the domain context save function would probably be worthwhile.
> >>
> >> And how does the initializer approach not fulfill that intention?
> >>
> >
> > Because any time anyone adds another field they have to remember to
> add
> > another initializer, which is what I forgot to do. This approach OTOH is
> > failsafe.
> 
> But note how I said "an empty initializer": When there is an
> initializer at all, all fields not mentioned in the initializer will get
> default initialized (i.e. zeroed). Hence an empty initializer
> clears the entire structure.
> 

Ah, you mean C99 initializer style. That would be neater.

  Paul

> Jan
Jan Beulich March 30, 2016, 3:24 p.m. UTC | #7
>>> On 30.03.16 at 17:16, <Paul.Durrant@citrix.com> wrote:
>>  -----Original Message-----
>> From: Jan Beulich [mailto:JBeulich@suse.com]
>> Sent: 30 March 2016 15:22
>> To: Paul Durrant
>> Cc: Andrew Cooper; xen-devel@lists.xenproject.org; Keir (Xen.org)
>> Subject: RE: [PATCH] x86/hvm/viridian: zero and check vcpu context __pad
>> field
>> 
>> >>> On 30.03.16 at 15:19, <Paul.Durrant@citrix.com> wrote:
>> >>  -----Original Message-----
>> >> From: Jan Beulich [mailto:JBeulich@suse.com]
>> >> Sent: 30 March 2016 14:17
>> >> To: Paul Durrant
>> >> Cc: Andrew Cooper; xen-devel@lists.xenproject.org; Keir (Xen.org)
>> >> Subject: RE: [PATCH] x86/hvm/viridian: zero and check vcpu context
>> __pad
>> >> field
>> >>
>> >> >>> On 30.03.16 at 13:26, <Paul.Durrant@citrix.com> wrote:
>> >> >> From: Jan Beulich [mailto:JBeulich@suse.com]
>> >> >> Sent: 30 March 2016 12:23
>> >> >> >>> On 30.03.16 at 12:32, <paul.durrant@citrix.com> wrote:
>> >> >> > --- a/xen/arch/x86/hvm/viridian.c
>> >> >> > +++ b/xen/arch/x86/hvm/viridian.c
>> >> >> > @@ -824,6 +824,8 @@ static int viridian_save_vcpu_ctxt(struct
>> domain
>> >> *d,
>> >> >> hvm_domain_context_t *h)
>> >> >> >      for_each_vcpu( d, v ) {
>> >> >> >          struct hvm_viridian_vcpu_context ctxt;
>> >> >> >
>> >> >> > +        memset(&ctxt, 0, sizeof(ctxt));
>> >> >>
>> >> >> How about just adding an empty initializer to the declaration?
>> >> >>
>> >> >
>> >> > I think having a 'zero the entire struct' call at the start is better as 
> it
>> >> > will cover any additions made to the struct in future. It's what I had
>> >> > mistakenly assumed was already there. In fact I think adding a similar 
> call
>> >> > into the domain context save function would probably be worthwhile.
>> >>
>> >> And how does the initializer approach not fulfill that intention?
>> >>
>> >
>> > Because any time anyone adds another field they have to remember to
>> add
>> > another initializer, which is what I forgot to do. This approach OTOH is
>> > failsafe.
>> 
>> But note how I said "an empty initializer": When there is an
>> initializer at all, all fields not mentioned in the initializer will get
>> default initialized (i.e. zeroed). Hence an empty initializer
>> clears the entire structure.
>> 
> 
> Ah, you mean C99 initializer style. That would be neater.

Not really - "static struct s s = {};" was allowed in C89 too iirc.

Jan
diff mbox

Patch

diff --git a/xen/arch/x86/hvm/viridian.c b/xen/arch/x86/hvm/viridian.c
index 5c76c1a..b85b55b 100644
--- a/xen/arch/x86/hvm/viridian.c
+++ b/xen/arch/x86/hvm/viridian.c
@@ -824,6 +824,8 @@  static int viridian_save_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h)
     for_each_vcpu( d, v ) {
         struct hvm_viridian_vcpu_context ctxt;
 
+        memset(&ctxt, 0, sizeof(ctxt));
+
         ctxt.apic_assist_msr = v->arch.hvm_vcpu.viridian.apic_assist.msr.raw;
         ctxt.apic_assist_vector = v->arch.hvm_vcpu.viridian.apic_assist.vector;
 
@@ -834,6 +836,15 @@  static int viridian_save_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h)
     return 0;
 }
 
+static bool_t is_zero(void *p, size_t size)
+{
+    while ( size-- )
+        if ( *(uint8_t *)p++ != 0 )
+            return 0;
+
+    return 1;
+}
+
 static int viridian_load_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h)
 {
     int vcpuid;
@@ -851,6 +862,9 @@  static int viridian_load_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h)
     if ( hvm_load_entry_zeroextend(VIRIDIAN_VCPU, h, &ctxt) != 0 )
         return -EINVAL;
 
+    if ( !is_zero(&ctxt._pad, sizeof(ctxt._pad)) )
+        return -EINVAL;
+
     v->arch.hvm_vcpu.viridian.apic_assist.msr.raw = ctxt.apic_assist_msr;
     if ( v->arch.hvm_vcpu.viridian.apic_assist.msr.fields.enabled )
         initialize_apic_assist(v);