diff mbox

[V2] xen/hvm: fix hypervisor crash with hvm_save_one()

Message ID 5909956E02000078001561D5@prv-mh.provo.novell.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Beulich May 3, 2017, 6:31 a.m. UTC
>>> On 02.05.17 at 18:11, <andrew.cooper3@citrix.com> wrote:
> On 02/05/17 17:02, Tim Deegan wrote:
>> At 18:21 +0300 on 02 May (1493749307), Razvan Cojocaru wrote:
>>> hvm_save_cpu_ctxt() returns success without writing any data into
>>> hvm_domain_context_t when all VCPUs are offline. This can then crash
>>> the hypervisor (with FATAL PAGE FAULT) in hvm_save_one() via the
>>> "off < (ctxt.cur - sizeof(*desc))" for() test, where ctxt.cur remains 0,
>>> causing an underflow which leads the hypervisor to go off the end of the
>>> ctxt buffer.
>> [...]
>>> Reported-by: Razvan Cojocaru <rcojocaru@bitdefender.com>
>>> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
>>> Signed-off-by: Razvan Cojocaru <rcojocaru@bitdefender.com>
>>> Tested-by: Razvan Cojocaru <rcojocaru@bitdefender.com>
>> I actually preferred the first patch
> 
> As did I.

Hmm, with both of you being of that opinion, I've taken another
look. I think I see now why you think that way (this being data
from an internal producer, overflow/underflow are not a primary
concern), so I'll withdraw my objection to the original patch (i.e.
I agree taking it with the v2 description). However, an alternative
would be


taking care of overflow/underflow (now consistently) as well, plus
avoiding the (imo ugly) goto without making the code harder to
read. Thoughts?

Jan

Comments

Tim Deegan May 3, 2017, 9:15 a.m. UTC | #1
At 00:31 -0600 on 03 May (1493771502), Jan Beulich wrote:
> Hmm, with both of you being of that opinion, I've taken another
> look. I think I see now why you think that way (this being data
> from an internal producer, overflow/underflow are not a primary
> concern), so I'll withdraw my objection to the original patch (i.e.
> I agree taking it with the v2 description). However, an alternative
> would be
> 
> --- unstable.orig/xen/common/hvm/save.c
> +++ unstable/xen/common/hvm/save.c
> @@ -79,14 +79,15 @@ size_t hvm_save_size(struct domain *d)
>  int hvm_save_one(struct domain *d, uint16_t typecode, uint16_t instance, 
>                   XEN_GUEST_HANDLE_64(uint8) handle)
>  {
> -    int rv = 0;
> +    int rv = -ENOENT;
>      size_t sz = 0;
>      struct vcpu *v;
>      hvm_domain_context_t ctxt = { 0, };
> +    const struct hvm_save_descriptor *desc;
>  
>      if ( d->is_dying 
>           || typecode > HVM_SAVE_CODE_MAX 
> -         || hvm_sr_handlers[typecode].size < sizeof(struct hvm_save_descriptor)
> +         || hvm_sr_handlers[typecode].size < sizeof(*desc)
>           || hvm_sr_handlers[typecode].save == NULL )
>          return -EINVAL;
>  
> @@ -107,12 +108,10 @@ int hvm_save_one(struct domain *d, uint1
>                 d->domain_id, typecode);
>          rv = -EFAULT;
>      }
> -    else
> +    else if ( ctxt.cur > sizeof(*desc) )
>      {
>          uint32_t off;
> -        const struct hvm_save_descriptor *desc;
>  
> -        rv = -ENOENT;
>          for ( off = 0; off < (ctxt.cur - sizeof(*desc)); off += desc->length )
>          {
>              desc = (void *)(ctxt.data + off);
> @@ -122,7 +121,8 @@ int hvm_save_one(struct domain *d, uint1
>              {
>                  uint32_t copy_length = desc->length;
>  
> -                if ( off + copy_length > ctxt.cur )
> +                if ( ctxt.cur < copy_length ||
> +                     off > ctxt.cur - copy_length )
>                      copy_length = ctxt.cur - off;
>                  rv = 0;
>                  if ( copy_to_guest(handle, ctxt.data + off, copy_length) )
> 
> taking care of overflow/underflow (now consistently) as well, plus
> avoiding the (imo ugly) goto without making the code harder to
> read. Thoughts?

Any of these three patches is fine by me.

Cheers,

Tim.
Razvan Cojocaru May 3, 2017, 9:20 a.m. UTC | #2
On 05/03/17 12:15, Tim Deegan wrote:
> At 00:31 -0600 on 03 May (1493771502), Jan Beulich wrote:
>> Hmm, with both of you being of that opinion, I've taken another
>> look. I think I see now why you think that way (this being data
>> from an internal producer, overflow/underflow are not a primary
>> concern), so I'll withdraw my objection to the original patch (i.e.
>> I agree taking it with the v2 description). However, an alternative
>> would be
>>
>> --- unstable.orig/xen/common/hvm/save.c
>> +++ unstable/xen/common/hvm/save.c
>> @@ -79,14 +79,15 @@ size_t hvm_save_size(struct domain *d)
>>  int hvm_save_one(struct domain *d, uint16_t typecode, uint16_t instance, 
>>                   XEN_GUEST_HANDLE_64(uint8) handle)
>>  {
>> -    int rv = 0;
>> +    int rv = -ENOENT;
>>      size_t sz = 0;
>>      struct vcpu *v;
>>      hvm_domain_context_t ctxt = { 0, };
>> +    const struct hvm_save_descriptor *desc;
>>  
>>      if ( d->is_dying 
>>           || typecode > HVM_SAVE_CODE_MAX 
>> -         || hvm_sr_handlers[typecode].size < sizeof(struct hvm_save_descriptor)
>> +         || hvm_sr_handlers[typecode].size < sizeof(*desc)
>>           || hvm_sr_handlers[typecode].save == NULL )
>>          return -EINVAL;
>>  
>> @@ -107,12 +108,10 @@ int hvm_save_one(struct domain *d, uint1
>>                 d->domain_id, typecode);
>>          rv = -EFAULT;
>>      }
>> -    else
>> +    else if ( ctxt.cur > sizeof(*desc) )
>>      {
>>          uint32_t off;
>> -        const struct hvm_save_descriptor *desc;
>>  
>> -        rv = -ENOENT;
>>          for ( off = 0; off < (ctxt.cur - sizeof(*desc)); off += desc->length )
>>          {
>>              desc = (void *)(ctxt.data + off);
>> @@ -122,7 +121,8 @@ int hvm_save_one(struct domain *d, uint1
>>              {
>>                  uint32_t copy_length = desc->length;
>>  
>> -                if ( off + copy_length > ctxt.cur )
>> +                if ( ctxt.cur < copy_length ||
>> +                     off > ctxt.cur - copy_length )
>>                      copy_length = ctxt.cur - off;
>>                  rv = 0;
>>                  if ( copy_to_guest(handle, ctxt.data + off, copy_length) )
>>
>> taking care of overflow/underflow (now consistently) as well, plus
>> avoiding the (imo ugly) goto without making the code harder to
>> read. Thoughts?
> 
> Any of these three patches is fine by me.

I feel the same. If Andrew prefers this version I'm happy to test it,
otherwise re-sending the first patch with the corrected description is
the fastest path.


Thanks,
Razvan
Tim Deegan May 3, 2017, 9:21 a.m. UTC | #3
At 10:15 +0100 on 03 May (1493806508), Tim Deegan wrote:
> At 00:31 -0600 on 03 May (1493771502), Jan Beulich wrote:
> > +    else if ( ctxt.cur > sizeof(*desc) )
> >      {
> >          uint32_t off;
> > -        const struct hvm_save_descriptor *desc;
> >  
> > -        rv = -ENOENT;
> >          for ( off = 0; off < (ctxt.cur - sizeof(*desc)); off += desc->length )

It occurs to me that as well as underflowing, this test is off by one.
It ought to be "off + sizeof(*desc) <= ctxt.cur" to allow for a
zero-length record.  AFAIK we don't actually have any of those, so
it's academic, but we might want to represent the presence of some
feature without having any feature-specific state to save.

Tim.
Jan Beulich May 3, 2017, 9:30 a.m. UTC | #4
>>> On 03.05.17 at 11:21, <tim@xen.org> wrote:
> At 10:15 +0100 on 03 May (1493806508), Tim Deegan wrote:
>> At 00:31 -0600 on 03 May (1493771502), Jan Beulich wrote:
>> > +    else if ( ctxt.cur > sizeof(*desc) )
>> >      {
>> >          uint32_t off;
>> > -        const struct hvm_save_descriptor *desc;
>> >  
>> > -        rv = -ENOENT;
>> >          for ( off = 0; off < (ctxt.cur - sizeof(*desc)); off += desc->length )
> 
> It occurs to me that as well as underflowing, this test is off by one.
> It ought to be "off + sizeof(*desc) <= ctxt.cur" to allow for a
> zero-length record.  AFAIK we don't actually have any of those, so
> it's academic, but we might want to represent the presence of some
> feature without having any feature-specific state to save.

Good point; I already have two follow-up patches, one of which I
think this adjustment would easily fit into.

Jan
Razvan Cojocaru May 3, 2017, 10:44 a.m. UTC | #5
On 05/03/17 12:30, Jan Beulich wrote:
>>>> On 03.05.17 at 11:21, <tim@xen.org> wrote:
>> At 10:15 +0100 on 03 May (1493806508), Tim Deegan wrote:
>>> At 00:31 -0600 on 03 May (1493771502), Jan Beulich wrote:
>>>> +    else if ( ctxt.cur > sizeof(*desc) )
>>>>      {
>>>>          uint32_t off;
>>>> -        const struct hvm_save_descriptor *desc;
>>>>  
>>>> -        rv = -ENOENT;
>>>>          for ( off = 0; off < (ctxt.cur - sizeof(*desc)); off += desc->length )
>>
>> It occurs to me that as well as underflowing, this test is off by one.
>> It ought to be "off + sizeof(*desc) <= ctxt.cur" to allow for a
>> zero-length record.  AFAIK we don't actually have any of those, so
>> it's academic, but we might want to represent the presence of some
>> feature without having any feature-specific state to save.
> 
> Good point; I already have two follow-up patches, one of which I
> think this adjustment would easily fit into.

Should I re-send the original patch with the updated comment then (thus
also being able to keep Andrew's Signed-off-by), and if so, is it
alright to keep Julien's Release-Acked-by?

Or will you use this later patch (presumably with your Signed-off-by),
in which case I should test it?

Things got rather confusing (apologies for my own part in the confusion).


Thanks,
Razvan
Andrew Cooper May 3, 2017, noon UTC | #6
On 03/05/17 11:44, Razvan Cojocaru wrote:
> On 05/03/17 12:30, Jan Beulich wrote:
>>>>> On 03.05.17 at 11:21, <tim@xen.org> wrote:
>>> At 10:15 +0100 on 03 May (1493806508), Tim Deegan wrote:
>>>> At 00:31 -0600 on 03 May (1493771502), Jan Beulich wrote:
>>>>> +    else if ( ctxt.cur > sizeof(*desc) )
>>>>>      {
>>>>>          uint32_t off;
>>>>> -        const struct hvm_save_descriptor *desc;
>>>>>  
>>>>> -        rv = -ENOENT;
>>>>>          for ( off = 0; off < (ctxt.cur - sizeof(*desc)); off += desc->length )
>>> It occurs to me that as well as underflowing, this test is off by one.
>>> It ought to be "off + sizeof(*desc) <= ctxt.cur" to allow for a
>>> zero-length record.  AFAIK we don't actually have any of those, so
>>> it's academic, but we might want to represent the presence of some
>>> feature without having any feature-specific state to save.
>> Good point; I already have two follow-up patches, one of which I
>> think this adjustment would easily fit into.
> Should I re-send the original patch with the updated comment then (thus
> also being able to keep Andrew's Signed-off-by), and if so, is it
> alright to keep Julien's Release-Acked-by?
>
> Or will you use this later patch (presumably with your Signed-off-by),
> in which case I should test it?
>
> Things got rather confusing (apologies for my own part in the confusion).

I am not opposed to Jan's alternative, but I think we should make the
adjustment to cope with zero length records.

At this point, it might be best for Jan to submit a complete patch and
for Razvan to double check that it still resolves the issue.

~Andrew
Jan Beulich May 3, 2017, 12:11 p.m. UTC | #7
>>> On 03.05.17 at 14:00, <andrew.cooper3@citrix.com> wrote:
> On 03/05/17 11:44, Razvan Cojocaru wrote:
>> On 05/03/17 12:30, Jan Beulich wrote:
>>>>>> On 03.05.17 at 11:21, <tim@xen.org> wrote:
>>>> At 10:15 +0100 on 03 May (1493806508), Tim Deegan wrote:
>>>>> At 00:31 -0600 on 03 May (1493771502), Jan Beulich wrote:
>>>>>> +    else if ( ctxt.cur > sizeof(*desc) )
>>>>>>      {
>>>>>>          uint32_t off;
>>>>>> -        const struct hvm_save_descriptor *desc;
>>>>>>  
>>>>>> -        rv = -ENOENT;
>>>>>>          for ( off = 0; off < (ctxt.cur - sizeof(*desc)); off += desc->length 
> )
>>>> It occurs to me that as well as underflowing, this test is off by one.
>>>> It ought to be "off + sizeof(*desc) <= ctxt.cur" to allow for a
>>>> zero-length record.  AFAIK we don't actually have any of those, so
>>>> it's academic, but we might want to represent the presence of some
>>>> feature without having any feature-specific state to save.
>>> Good point; I already have two follow-up patches, one of which I
>>> think this adjustment would easily fit into.
>> Should I re-send the original patch with the updated comment then (thus
>> also being able to keep Andrew's Signed-off-by), and if so, is it
>> alright to keep Julien's Release-Acked-by?
>>
>> Or will you use this later patch (presumably with your Signed-off-by),
>> in which case I should test it?
>>
>> Things got rather confusing (apologies for my own part in the confusion).
> 
> I am not opposed to Jan's alternative, but I think we should make the
> adjustment to cope with zero length records.

I can certainly move this here from the follow-up patch.

> At this point, it might be best for Jan to submit a complete patch and
> for Razvan to double check that it still resolves the issue.

Let me do that then.

Jan
diff mbox

Patch

--- unstable.orig/xen/common/hvm/save.c
+++ unstable/xen/common/hvm/save.c
@@ -79,14 +79,15 @@  size_t hvm_save_size(struct domain *d)
 int hvm_save_one(struct domain *d, uint16_t typecode, uint16_t instance, 
                  XEN_GUEST_HANDLE_64(uint8) handle)
 {
-    int rv = 0;
+    int rv = -ENOENT;
     size_t sz = 0;
     struct vcpu *v;
     hvm_domain_context_t ctxt = { 0, };
+    const struct hvm_save_descriptor *desc;
 
     if ( d->is_dying 
          || typecode > HVM_SAVE_CODE_MAX 
-         || hvm_sr_handlers[typecode].size < sizeof(struct hvm_save_descriptor)
+         || hvm_sr_handlers[typecode].size < sizeof(*desc)
          || hvm_sr_handlers[typecode].save == NULL )
         return -EINVAL;
 
@@ -107,12 +108,10 @@  int hvm_save_one(struct domain *d, uint1
                d->domain_id, typecode);
         rv = -EFAULT;
     }
-    else
+    else if ( ctxt.cur > sizeof(*desc) )
     {
         uint32_t off;
-        const struct hvm_save_descriptor *desc;
 
-        rv = -ENOENT;
         for ( off = 0; off < (ctxt.cur - sizeof(*desc)); off += desc->length )
         {
             desc = (void *)(ctxt.data + off);
@@ -122,7 +121,8 @@  int hvm_save_one(struct domain *d, uint1
             {
                 uint32_t copy_length = desc->length;
 
-                if ( off + copy_length > ctxt.cur )
+                if ( ctxt.cur < copy_length ||
+                     off > ctxt.cur - copy_length )
                     copy_length = ctxt.cur - off;
                 rv = 0;
                 if ( copy_to_guest(handle, ctxt.data + off, copy_length) )