diff mbox

[07/23] hw/pci/pcie_aer.c: fix buffer overruns on invalid state load

Message ID 1386087086-3691-8-git-send-email-mst@redhat.com
State New
Headers show

Commit Message

Michael S. Tsirkin Dec. 3, 2013, 4:28 p.m. UTC
4) CVE-2013-4529
hw/pci/pcie_aer.c    pcie aer log can overrun the buffer if log_num is
                     too large

There are two issues in this file:
1. log_max from remote can be larger than on local
then buffer will overrun with data coming from state file.
2. log_num can be larger then we get data corrution
again with an overflow but not adversary controlled.

Fix both issues.

Reported-by: Anthony Liguori <anthony@codemonkey.ws>
Reported-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 hw/pci/pcie_aer.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

Comments

Peter Maydell Dec. 3, 2013, 6:30 p.m. UTC | #1
On 3 December 2013 16:28, Michael S. Tsirkin <mst@redhat.com> wrote:
> 4) CVE-2013-4529
> hw/pci/pcie_aer.c    pcie aer log can overrun the buffer if log_num is
>                      too large
>
> There are two issues in this file:
> 1. log_max from remote can be larger than on local
> then buffer will overrun with data coming from state file.
> 2. log_num can be larger then we get data corrution
> again with an overflow but not adversary controlled.
>
> Fix both issues.
>
> Reported-by: Anthony Liguori <anthony@codemonkey.ws>
> Reported-by: Michael S. Tsirkin <mst@redhat.com>
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> ---
>  hw/pci/pcie_aer.c | 15 +++++++++++++--
>  1 file changed, 13 insertions(+), 2 deletions(-)
>
> diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c
> index 991502e..92f3f20 100644
> --- a/hw/pci/pcie_aer.c
> +++ b/hw/pci/pcie_aer.c
> @@ -795,15 +795,26 @@ static const VMStateDescription vmstate_pcie_aer_err = {
>      }
>  };
>
> +static int pcie_aer_state_post_load(void *opaque, int version_id)
> +{
> +    PCIEAERLog *s = opaque;
> +
> +    if (s->log_num > s->log_max) {
> +        return -1;
> +    }
> +    return 0;
> +}
> +
>  const VMStateDescription vmstate_pcie_aer_log = {
>      .name = "PCIE_AER_ERROR_LOG",
>      .version_id = 1,
>      .minimum_version_id = 1,
>      .minimum_version_id_old = 1,
> +    .post_load = pcie_aer_state_post_load,
>      .fields     = (VMStateField[]) {
>          VMSTATE_UINT16(log_num, PCIEAERLog),
> -        VMSTATE_UINT16(log_max, PCIEAERLog),
> -        VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num,
> +        VMSTATE_UINT16_EQUAL(log_max, PCIEAERLog),
> +        VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_max,
>                                vmstate_pcie_aer_err, PCIEAERErr),
>          VMSTATE_END_OF_LIST()
>      }

Isn't this a migration compability break? If so the version ID
needs to be bumped.

thanks
-- PMM
Michael S. Tsirkin Dec. 3, 2013, 8:41 p.m. UTC | #2
On Tue, Dec 03, 2013 at 06:30:46PM +0000, Peter Maydell wrote:
> On 3 December 2013 16:28, Michael S. Tsirkin <mst@redhat.com> wrote:
> > 4) CVE-2013-4529
> > hw/pci/pcie_aer.c    pcie aer log can overrun the buffer if log_num is
> >                      too large
> >
> > There are two issues in this file:
> > 1. log_max from remote can be larger than on local
> > then buffer will overrun with data coming from state file.
> > 2. log_num can be larger then we get data corrution
> > again with an overflow but not adversary controlled.
> >
> > Fix both issues.
> >
> > Reported-by: Anthony Liguori <anthony@codemonkey.ws>
> > Reported-by: Michael S. Tsirkin <mst@redhat.com>
> > Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> > ---
> >  hw/pci/pcie_aer.c | 15 +++++++++++++--
> >  1 file changed, 13 insertions(+), 2 deletions(-)
> >
> > diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c
> > index 991502e..92f3f20 100644
> > --- a/hw/pci/pcie_aer.c
> > +++ b/hw/pci/pcie_aer.c
> > @@ -795,15 +795,26 @@ static const VMStateDescription vmstate_pcie_aer_err = {
> >      }
> >  };
> >
> > +static int pcie_aer_state_post_load(void *opaque, int version_id)
> > +{
> > +    PCIEAERLog *s = opaque;
> > +
> > +    if (s->log_num > s->log_max) {
> > +        return -1;
> > +    }
> > +    return 0;
> > +}
> > +
> >  const VMStateDescription vmstate_pcie_aer_log = {
> >      .name = "PCIE_AER_ERROR_LOG",
> >      .version_id = 1,
> >      .minimum_version_id = 1,
> >      .minimum_version_id_old = 1,
> > +    .post_load = pcie_aer_state_post_load,
> >      .fields     = (VMStateField[]) {
> >          VMSTATE_UINT16(log_num, PCIEAERLog),
> > -        VMSTATE_UINT16(log_max, PCIEAERLog),
> > -        VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num,
> > +        VMSTATE_UINT16_EQUAL(log_max, PCIEAERLog),
> > +        VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_max,
> >                                vmstate_pcie_aer_err, PCIEAERErr),
> >          VMSTATE_END_OF_LIST()
> >      }
> 
> Isn't this a migration compability break?

How is it a break?

> If so the version ID
> needs to be bumped.
> 
> thanks
> -- PMM
Peter Maydell Dec. 3, 2013, 8:59 p.m. UTC | #3
On 3 December 2013 20:41, Michael S. Tsirkin <mst@redhat.com> wrote:
> On Tue, Dec 03, 2013 at 06:30:46PM +0000, Peter Maydell wrote:
>> On 3 December 2013 16:28, Michael S. Tsirkin <mst@redhat.com> wrote:
>> >  const VMStateDescription vmstate_pcie_aer_log = {
>> >      .name = "PCIE_AER_ERROR_LOG",
>> >      .version_id = 1,
>> >      .minimum_version_id = 1,
>> >      .minimum_version_id_old = 1,
>> > +    .post_load = pcie_aer_state_post_load,
>> >      .fields     = (VMStateField[]) {
>> >          VMSTATE_UINT16(log_num, PCIEAERLog),
>> > -        VMSTATE_UINT16(log_max, PCIEAERLog),
>> > -        VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num,
>> > +        VMSTATE_UINT16_EQUAL(log_max, PCIEAERLog),
>> > +        VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_max,
>> >                                vmstate_pcie_aer_err, PCIEAERErr),
>> >          VMSTATE_END_OF_LIST()
>> >      }
>>
>> Isn't this a migration compability break?
>
> How is it a break?

If a QEMU with this patch sends data to a QEMU without it, then the
receiving end will think it should expect log_num array entries but the
sending end is going to send log_max of them. Conversely, an old->new
migration is going to send fewer array entries than the destination
expects. Or have I misinterpreted how the VARRAY entries work?

thanks
-- PMM
Eric Blake Dec. 3, 2013, 9:19 p.m. UTC | #4
On 12/03/2013 01:59 PM, Peter Maydell wrote:

> 
> If a QEMU with this patch sends data to a QEMU without it, then the
> receiving end will think it should expect log_num array entries but the
> sending end is going to send log_max of them. Conversely, an old->new
> migration is going to send fewer array entries than the destination
> expects. Or have I misinterpreted how the VARRAY entries work?

If a qemu sends data larger than the field, the source side is already
compromised.  All we care about is that the destination fails
gracefully, rather than acting on the bogus information from the
compromised source.  Versioning is only necessary for correct migration
data, and doesn't matter when the source is already compromised.
Peter Maydell Dec. 3, 2013, 9:25 p.m. UTC | #5
On 3 December 2013 21:19, Eric Blake <eblake@redhat.com> wrote:
> On 12/03/2013 01:59 PM, Peter Maydell wrote:
>
>>
>> If a QEMU with this patch sends data to a QEMU without it, then the
>> receiving end will think it should expect log_num array entries but the
>> sending end is going to send log_max of them. Conversely, an old->new
>> migration is going to send fewer array entries than the destination
>> expects. Or have I misinterpreted how the VARRAY entries work?
>
> If a qemu sends data larger than the field, the source side is already
> compromised.

Not if the reason it's sending data larger than the field is because
it's a non-compromised QEMU with this patch which makes it send
log_max entries regardless of log_num, surely?

-- PMM
Michael S. Tsirkin Dec. 4, 2013, 8:40 a.m. UTC | #6
On Tue, Dec 03, 2013 at 08:59:52PM +0000, Peter Maydell wrote:
> On 3 December 2013 20:41, Michael S. Tsirkin <mst@redhat.com> wrote:
> > On Tue, Dec 03, 2013 at 06:30:46PM +0000, Peter Maydell wrote:
> >> On 3 December 2013 16:28, Michael S. Tsirkin <mst@redhat.com> wrote:
> >> >  const VMStateDescription vmstate_pcie_aer_log = {
> >> >      .name = "PCIE_AER_ERROR_LOG",
> >> >      .version_id = 1,
> >> >      .minimum_version_id = 1,
> >> >      .minimum_version_id_old = 1,
> >> > +    .post_load = pcie_aer_state_post_load,
> >> >      .fields     = (VMStateField[]) {
> >> >          VMSTATE_UINT16(log_num, PCIEAERLog),
> >> > -        VMSTATE_UINT16(log_max, PCIEAERLog),
> >> > -        VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num,
> >> > +        VMSTATE_UINT16_EQUAL(log_max, PCIEAERLog),
> >> > +        VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_max,
> >> >                                vmstate_pcie_aer_err, PCIEAERErr),
> >> >          VMSTATE_END_OF_LIST()
> >> >      }
> >>
> >> Isn't this a migration compability break?
> >
> > How is it a break?
> 
> If a QEMU with this patch sends data to a QEMU without it, then the
> receiving end will think it should expect log_num array entries but the
> sending end is going to send log_max of them. Conversely, an old->new
> migration is going to send fewer array entries than the destination
> expects. Or have I misinterpreted how the VARRAY entries work?
> 
> thanks
> -- PMM

Ah, got it. You are right, good catch. I think we need VMSTATE_UINT16_LE, this will
make sure log_num <= log_max without changing
VMSTATE_STRUCT_VARRAY size.
diff mbox

Patch

diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c
index 991502e..92f3f20 100644
--- a/hw/pci/pcie_aer.c
+++ b/hw/pci/pcie_aer.c
@@ -795,15 +795,26 @@  static const VMStateDescription vmstate_pcie_aer_err = {
     }
 };
 
+static int pcie_aer_state_post_load(void *opaque, int version_id)
+{
+    PCIEAERLog *s = opaque;
+
+    if (s->log_num > s->log_max) {
+        return -1;
+    }
+    return 0;
+}
+
 const VMStateDescription vmstate_pcie_aer_log = {
     .name = "PCIE_AER_ERROR_LOG",
     .version_id = 1,
     .minimum_version_id = 1,
     .minimum_version_id_old = 1,
+    .post_load = pcie_aer_state_post_load,
     .fields     = (VMStateField[]) {
         VMSTATE_UINT16(log_num, PCIEAERLog),
-        VMSTATE_UINT16(log_max, PCIEAERLog),
-        VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num,
+        VMSTATE_UINT16_EQUAL(log_max, PCIEAERLog),
+        VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_max,
                               vmstate_pcie_aer_err, PCIEAERErr),
         VMSTATE_END_OF_LIST()
     }