diff mbox

[v4,15/30] stellaris_enet: avoid buffer orerrun on incoming migration (part 3)

Message ID 1396275242-10810-16-git-send-email-mst@redhat.com
State New
Headers show

Commit Message

Michael S. Tsirkin March 31, 2014, 2:16 p.m. UTC
CVE-2013-4532

s->tx_frame_len is read from the wire and can later used as an index
into s->tx_fifo[] for memset() when a DATA command is issued by the guest.

In this case s->tx_frame_len is checked to avoid an overrun, but if the
value is negative a subsequently executed guest can underrun the buffer
with zeros via the memset() call.

Additionally, tx_frame_len is used to validate that tx_fifo_len
doesn't exceed the fifo bounds - the assumption being that data model
never makes it exceed 2032.

Fix this by failing migration if the incoming value of s->tx_frame_len
is less than -1 (the emulation code allows from -1 as a special case)
or if it exceeds 2032.

Reported-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Reported-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 hw/net/stellaris_enet.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

Comments

Dr. David Alan Gilbert April 1, 2014, 9:51 a.m. UTC | #1
* Michael S. Tsirkin (mst@redhat.com) wrote:
> CVE-2013-4532
> 
> s->tx_frame_len is read from the wire and can later used as an index
> into s->tx_fifo[] for memset() when a DATA command is issued by the guest.
> 
> In this case s->tx_frame_len is checked to avoid an overrun, but if the
> value is negative a subsequently executed guest can underrun the buffer
> with zeros via the memset() call.
> 
> Additionally, tx_frame_len is used to validate that tx_fifo_len
> doesn't exceed the fifo bounds - the assumption being that data model
> never makes it exceed 2032.
> 
> Fix this by failing migration if the incoming value of s->tx_frame_len
> is less than -1 (the emulation code allows from -1 as a special case)
> or if it exceeds 2032.
> 
> Reported-by: Michael Roth <mdroth@linux.vnet.ibm.com>
> Reported-by: Peter Maydell <peter.maydell@linaro.org>
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> ---
>  hw/net/stellaris_enet.c | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c
> index aed00fd..90ff950 100644
> --- a/hw/net/stellaris_enet.c
> +++ b/hw/net/stellaris_enet.c
> @@ -373,7 +373,11 @@ static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id)
>      s->mtxd = qemu_get_be32(f);
>      s->mrxd = qemu_get_be32(f);
>      s->np = qemu_get_be32(f);
> -    s->tx_frame_len = qemu_get_be32(f);
> +    v = qemu_get_be32(f);
> +    if (v < -1 || s->tx_frame_len > 2032) {
> +        return -EINVAL;
> +    }
> +    s->tx_frame_len = v;

I think that's wrong; 'stellaris_enet_write' does the following:

            s->tx_frame_len = value & 0xffff;
            if (s->tx_frame_len > 2032) {
                DPRINTF("TX frame too long (%d)\n", s->tx_frame_len);
                s->tx_frame_len = 0;
                s->ris |= SE_INT_TXER;
                stellaris_enet_update(s);
            } else {
                DPRINTF("Start TX frame len=%d\n", s->tx_frame_len);
                /* The value written does not include the ethernet header.  */
                s->tx_frame_len += 14;
                if ((s->tctl & SE_TCTL_CRC) == 0)
                    s->tx_frame_len += 4;

So lets say that tx_frame_len is initially 2032 when written; 14 is added to it
at this point, and if the CRC flag is set then another 4.   Thus it seems a user
can set the value in tx_frame_len to 2032+14+4=2050  - which is a bit worrying
given the buffer is only 2048 bytes.

Dave

>      v = qemu_get_be32(f);
>      /* How many bytes does data use in tx fifo. */
>      sz = s->tx_frame_len == -1 ? 2 : 4;
> -- 
> MST
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Peter Maydell April 1, 2014, 10:06 a.m. UTC | #2
On 1 April 2014 10:51, Dr. David Alan Gilbert <dgilbert@redhat.com> wrote:
> So lets say that tx_frame_len is initially 2032 when written; 14 is added to it
> at this point, and if the CRC flag is set then another 4.   Thus it seems a user
> can set the value in tx_frame_len to 2032+14+4=2050  - which is a bit worrying
> given the buffer is only 2048 bytes.


Yep, see my equivalent remarks in the other patch.

Michael -- can we please squash these two patches into one?
It's really hard to review the code for correctness when
half the logic for dealing with the tx fifo is in a
different patch...

thanks
-- PMM
Eric Blake April 1, 2014, 2:42 p.m. UTC | #3
On 03/31/2014 08:16 AM, Michael S. Tsirkin wrote:
> CVE-2013-4532

s/orerrun/overrun/ in the subject
Michael S. Tsirkin April 1, 2014, 3:22 p.m. UTC | #4
On Tue, Apr 01, 2014 at 11:06:48AM +0100, Peter Maydell wrote:
> On 1 April 2014 10:51, Dr. David Alan Gilbert <dgilbert@redhat.com> wrote:
> > So lets say that tx_frame_len is initially 2032 when written; 14 is added to it
> > at this point, and if the CRC flag is set then another 4.   Thus it seems a user
> > can set the value in tx_frame_len to 2032+14+4=2050  - which is a bit worrying
> > given the buffer is only 2048 bytes.
> 
> 
> Yep, see my equivalent remarks in the other patch.
> 
> Michael -- can we please squash these two patches into one?
> It's really hard to review the code for correctness when
> half the logic for dealing with the tx fifo is in a
> different patch...
> 
> thanks
> -- PMM

Will do - part 1 as well?
Peter Maydell April 1, 2014, 3:56 p.m. UTC | #5
On 1 April 2014 16:22, Michael S. Tsirkin <mst@redhat.com> wrote:
> On Tue, Apr 01, 2014 at 11:06:48AM +0100, Peter Maydell wrote:
>> Michael -- can we please squash these two patches into one?
>> It's really hard to review the code for correctness when
>> half the logic for dealing with the tx fifo is in a
>> different patch...

> Will do - part 1 as well?

No, that's OK, it's dealing with the rx logic rather than
the tx logic.

However, I've been writing some patches this afternoon
which get rid of tx_frame_len completely, so you might
want to wait for those...

thanks
-- PMM
diff mbox

Patch

diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c
index aed00fd..90ff950 100644
--- a/hw/net/stellaris_enet.c
+++ b/hw/net/stellaris_enet.c
@@ -373,7 +373,11 @@  static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id)
     s->mtxd = qemu_get_be32(f);
     s->mrxd = qemu_get_be32(f);
     s->np = qemu_get_be32(f);
-    s->tx_frame_len = qemu_get_be32(f);
+    v = qemu_get_be32(f);
+    if (v < -1 || s->tx_frame_len > 2032) {
+        return -EINVAL;
+    }
+    s->tx_frame_len = v;
     v = qemu_get_be32(f);
     /* How many bytes does data use in tx fifo. */
     sz = s->tx_frame_len == -1 ? 2 : 4;