qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] e1000: eliminate infinite loops on out-of-bounds transfer start
@ 2016-01-19 13:17 Laszlo Ersek
  2016-01-22  3:09 ` Jason Wang
  0 siblings, 1 reply; 8+ messages in thread
From: Laszlo Ersek @ 2016-01-19 13:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Petr Matousek, Stefano Stabellini, Jason Wang,
	Michael S. Tsirkin, Michael Roth, Prasad Pandit

The start_xmit() and e1000_receive_iov() functions implement DMA transfers
iterating over a set of descriptors that the guest's e1000 driver
prepares:

- the TDLEN and RDLEN registers store the total size of the descriptor
  area,

- while the TDH and RDH registers store the offset (in whole tx / rx
  descriptors) into the area where the transfer is supposed to start.

Each time a descriptor is processed, the TDH and RDH register is bumped
(as appropriate for the transfer direction).

QEMU already contains logic to deal with bogus transfers submitted by the
guest:

- Normally, the transmit case wants to increase TDH from its initial value
  to TDT. (TDT is allowed to be numerically smaller than the initial TDH
  value; wrapping at or above TDLEN bytes to zero is normal.) The failsafe
  that QEMU currently has here is a check against reaching the original
  TDH value again -- a complete wraparound, which should never happen.

- In the receive case RDH is increased from its initial value until
  "total_size" bytes have been received; preferably in a single step, or
  in "s->rxbuf_size" byte steps, if the latter is smaller. However, null
  RX descriptors are skipped without receiving data, while RDH is
  incremented just the same. QEMU tries to prevent an infinite loop
  (processing only null RX descriptors) by detecting whether RDH assumes
  its original value during the loop. (Again, wrapping from RDLEN to 0 is
  normal.)

What both directions miss is that the guest could program TDLEN and RDLEN
so low, and the initial TDH and RDH so high, that these registers will
immediately be truncated to zero, and then never reassume their initial
values in the loop -- a full wraparound will never occur.

The condition that expresses this is:

  xdh_start >= s->mac_reg[XDLEN] / sizeof(desc)

i.e., TDH or RDH start out after the last whole rx or tx descriptor that
fits into the TDLEN or RDLEN sized area.

This condition could be checked before we enter the loops, but
pci_dma_read() / pci_dma_write() knows how to fill in buffers safely for
bogus DMA addresses, so we just extend the existing failsafes with the
above condition.

Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Petr Matousek <pmatouse@redhat.com>
Cc: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Cc: Prasad Pandit <ppandit@redhat.com>
Cc: Michael Roth <mdroth@linux.vnet.ibm.com>
Cc: Jason Wang <jasowang@redhat.com>
RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1296044
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jason Wang <jasowang@redhat.com>
---

Notes:
    Regarding the public posting: we made an honest effort to vet this
    vulnerability, and the impact seems low -- no host side reads/writes,
    "just" a DoS (infinite loop). We decided the patch could be posted
    publicly, for the usual review process. Jason and Prasad checked the
    patch in the internal discussion already, but comments, improvements
    etc. are clearly welcome. The CVE request is underway. Thanks.

 hw/net/e1000.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index bec06e9..34d0823 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -908,7 +908,8 @@ start_xmit(E1000State *s)
          * bogus values to TDT/TDLEN.
          * there's nothing too intelligent we could do about this.
          */
-        if (s->mac_reg[TDH] == tdh_start) {
+        if (s->mac_reg[TDH] == tdh_start ||
+            tdh_start >= s->mac_reg[TDLEN] / sizeof(desc)) {
             DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n",
                    tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]);
             break;
@@ -1165,7 +1166,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
         if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN])
             s->mac_reg[RDH] = 0;
         /* see comment in start_xmit; same here */
-        if (s->mac_reg[RDH] == rdh_start) {
+        if (s->mac_reg[RDH] == rdh_start ||
+            rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) {
             DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n",
                    rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]);
             set_ics(s, 0, E1000_ICS_RXO);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH] e1000: eliminate infinite loops on out-of-bounds transfer start
  2016-01-19 13:17 [Qemu-devel] [PATCH] e1000: eliminate infinite loops on out-of-bounds transfer start Laszlo Ersek
@ 2016-01-22  3:09 ` Jason Wang
  2016-01-22  6:11   ` Michael Tokarev
  0 siblings, 1 reply; 8+ messages in thread
From: Jason Wang @ 2016-01-22  3:09 UTC (permalink / raw)
  To: Laszlo Ersek, qemu-devel
  Cc: Prasad Pandit, Stefano Stabellini, Petr Matousek, Michael Roth,
	Michael S. Tsirkin



On 01/19/2016 09:17 PM, Laszlo Ersek wrote:
> The start_xmit() and e1000_receive_iov() functions implement DMA transfers
> iterating over a set of descriptors that the guest's e1000 driver
> prepares:
>
> - the TDLEN and RDLEN registers store the total size of the descriptor
>   area,
>
> - while the TDH and RDH registers store the offset (in whole tx / rx
>   descriptors) into the area where the transfer is supposed to start.
>
> Each time a descriptor is processed, the TDH and RDH register is bumped
> (as appropriate for the transfer direction).
>
> QEMU already contains logic to deal with bogus transfers submitted by the
> guest:
>
> - Normally, the transmit case wants to increase TDH from its initial value
>   to TDT. (TDT is allowed to be numerically smaller than the initial TDH
>   value; wrapping at or above TDLEN bytes to zero is normal.) The failsafe
>   that QEMU currently has here is a check against reaching the original
>   TDH value again -- a complete wraparound, which should never happen.
>
> - In the receive case RDH is increased from its initial value until
>   "total_size" bytes have been received; preferably in a single step, or
>   in "s->rxbuf_size" byte steps, if the latter is smaller. However, null
>   RX descriptors are skipped without receiving data, while RDH is
>   incremented just the same. QEMU tries to prevent an infinite loop
>   (processing only null RX descriptors) by detecting whether RDH assumes
>   its original value during the loop. (Again, wrapping from RDLEN to 0 is
>   normal.)
>
> What both directions miss is that the guest could program TDLEN and RDLEN
> so low, and the initial TDH and RDH so high, that these registers will
> immediately be truncated to zero, and then never reassume their initial
> values in the loop -- a full wraparound will never occur.
>
> The condition that expresses this is:
>
>   xdh_start >= s->mac_reg[XDLEN] / sizeof(desc)
>
> i.e., TDH or RDH start out after the last whole rx or tx descriptor that
> fits into the TDLEN or RDLEN sized area.
>
> This condition could be checked before we enter the loops, but
> pci_dma_read() / pci_dma_write() knows how to fill in buffers safely for
> bogus DMA addresses, so we just extend the existing failsafes with the
> above condition.
>
> Cc: "Michael S. Tsirkin" <mst@redhat.com>
> Cc: Petr Matousek <pmatouse@redhat.com>
> Cc: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
> Cc: Prasad Pandit <ppandit@redhat.com>
> Cc: Michael Roth <mdroth@linux.vnet.ibm.com>
> Cc: Jason Wang <jasowang@redhat.com>
> RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1296044
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> Reviewed-by: Jason Wang <jasowang@redhat.com>
> ---
>
> Notes:
>     Regarding the public posting: we made an honest effort to vet this
>     vulnerability, and the impact seems low -- no host side reads/writes,
>     "just" a DoS (infinite loop). We decided the patch could be posted
>     publicly, for the usual review process. Jason and Prasad checked the
>     patch in the internal discussion already, but comments, improvements
>     etc. are clearly welcome. The CVE request is underway. Thanks.
>
>  hw/net/e1000.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/hw/net/e1000.c b/hw/net/e1000.c
> index bec06e9..34d0823 100644
> --- a/hw/net/e1000.c
> +++ b/hw/net/e1000.c
> @@ -908,7 +908,8 @@ start_xmit(E1000State *s)
>           * bogus values to TDT/TDLEN.
>           * there's nothing too intelligent we could do about this.
>           */
> -        if (s->mac_reg[TDH] == tdh_start) {
> +        if (s->mac_reg[TDH] == tdh_start ||
> +            tdh_start >= s->mac_reg[TDLEN] / sizeof(desc)) {
>              DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n",
>                     tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]);
>              break;
> @@ -1165,7 +1166,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
>          if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN])
>              s->mac_reg[RDH] = 0;
>          /* see comment in start_xmit; same here */
> -        if (s->mac_reg[RDH] == rdh_start) {
> +        if (s->mac_reg[RDH] == rdh_start ||
> +            rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) {
>              DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n",
>                     rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]);
>              set_ics(s, 0, E1000_ICS_RXO);

Applied in my -net.

Thanks

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH] e1000: eliminate infinite loops on out-of-bounds transfer start
  2016-01-22  3:09 ` Jason Wang
@ 2016-01-22  6:11   ` Michael Tokarev
  2016-01-22  6:15     ` Jason Wang
  0 siblings, 1 reply; 8+ messages in thread
From: Michael Tokarev @ 2016-01-22  6:11 UTC (permalink / raw)
  To: Jason Wang, Laszlo Ersek, qemu-devel
  Cc: Michael S. Tsirkin, Stefano Stabellini, Petr Matousek,
	Michael Roth, Prasad Pandit

22.01.2016 06:09, Jason Wang wrote:
> On 01/19/2016 09:17 PM, Laszlo Ersek wrote:
>> The start_xmit() and e1000_receive_iov() functions implement DMA transfers
>> iterating over a set of descriptors that the guest's e1000 driver
>> prepares:
...
> Applied in my -net.

This is CVE-2016-1981, btw.

/mjt

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH] e1000: eliminate infinite loops on out-of-bounds transfer start
  2016-01-22  6:11   ` Michael Tokarev
@ 2016-01-22  6:15     ` Jason Wang
  2016-01-22  9:09       ` Laszlo Ersek
  2016-01-27 18:35       ` Laszlo Ersek
  0 siblings, 2 replies; 8+ messages in thread
From: Jason Wang @ 2016-01-22  6:15 UTC (permalink / raw)
  To: Michael Tokarev, Laszlo Ersek, qemu-devel
  Cc: Prasad Pandit, Stefano Stabellini, Petr Matousek, Michael Roth,
	Michael S. Tsirkin



On 01/22/2016 02:11 PM, Michael Tokarev wrote:
> 22.01.2016 06:09, Jason Wang wrote:
>> On 01/19/2016 09:17 PM, Laszlo Ersek wrote:
>>> The start_xmit() and e1000_receive_iov() functions implement DMA transfers
>>> iterating over a set of descriptors that the guest's e1000 driver
>>> prepares:
> ...
>> Applied in my -net.
> This is CVE-2016-1981, btw.
>
> /mjt
>

Add this into commit log.

Thanks

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH] e1000: eliminate infinite loops on out-of-bounds transfer start
  2016-01-22  6:15     ` Jason Wang
@ 2016-01-22  9:09       ` Laszlo Ersek
  2016-01-27 18:35       ` Laszlo Ersek
  1 sibling, 0 replies; 8+ messages in thread
From: Laszlo Ersek @ 2016-01-22  9:09 UTC (permalink / raw)
  To: Jason Wang, Michael Tokarev, qemu-devel
  Cc: Prasad Pandit, Stefano Stabellini, Petr Matousek, Michael Roth,
	Michael S. Tsirkin

On 01/22/16 07:15, Jason Wang wrote:
> 
> 
> On 01/22/2016 02:11 PM, Michael Tokarev wrote:
>> 22.01.2016 06:09, Jason Wang wrote:
>>> On 01/19/2016 09:17 PM, Laszlo Ersek wrote:
>>>> The start_xmit() and e1000_receive_iov() functions implement DMA transfers
>>>> iterating over a set of descriptors that the guest's e1000 driver
>>>> prepares:
>> ...
>>> Applied in my -net.
>> This is CVE-2016-1981, btw.
>>
>> /mjt
>>
> 
> Add this into commit log.

Thanks guys!
Laszlo

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH] e1000: eliminate infinite loops on out-of-bounds transfer start
  2016-01-22  6:15     ` Jason Wang
  2016-01-22  9:09       ` Laszlo Ersek
@ 2016-01-27 18:35       ` Laszlo Ersek
  2016-01-28  5:47         ` Jason Wang
  1 sibling, 1 reply; 8+ messages in thread
From: Laszlo Ersek @ 2016-01-27 18:35 UTC (permalink / raw)
  To: Jason Wang
  Cc: Petr Matousek, Stefano Stabellini, Michael S. Tsirkin,
	Michael Tokarev, Michael Roth, qemu-devel, Prasad Pandit

Hello Jason,

On 01/22/16 07:15, Jason Wang wrote:
> 
> 
> On 01/22/2016 02:11 PM, Michael Tokarev wrote:
>> 22.01.2016 06:09, Jason Wang wrote:
>>> On 01/19/2016 09:17 PM, Laszlo Ersek wrote:
>>>> The start_xmit() and e1000_receive_iov() functions implement DMA transfers
>>>> iterating over a set of descriptors that the guest's e1000 driver
>>>> prepares:
>> ...
>>> Applied in my -net.
>> This is CVE-2016-1981, btw.
>>
>> /mjt
>>
> 
> Add this into commit log.

do you plan to send a PULL req soon? The patch is not really urgent, but
it would help me move forward with my queue.

Thanks!
Laszlo

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH] e1000: eliminate infinite loops on out-of-bounds transfer start
  2016-01-27 18:35       ` Laszlo Ersek
@ 2016-01-28  5:47         ` Jason Wang
  2016-01-28 11:09           ` Laszlo Ersek
  0 siblings, 1 reply; 8+ messages in thread
From: Jason Wang @ 2016-01-28  5:47 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: Petr Matousek, Michael S. Tsirkin, Stefano Stabellini,
	Michael Tokarev, qemu-devel, Michael Roth, Prasad Pandit



On 01/28/2016 02:35 AM, Laszlo Ersek wrote:
> Hello Jason,
>
> On 01/22/16 07:15, Jason Wang wrote:
>>
>> On 01/22/2016 02:11 PM, Michael Tokarev wrote:
>>> 22.01.2016 06:09, Jason Wang wrote:
>>>> On 01/19/2016 09:17 PM, Laszlo Ersek wrote:
>>>>> The start_xmit() and e1000_receive_iov() functions implement DMA transfers
>>>>> iterating over a set of descriptors that the guest's e1000 driver
>>>>> prepares:
>>> ...
>>>> Applied in my -net.
>>> This is CVE-2016-1981, btw.
>>>
>>> /mjt
>>>
>> Add this into commit log.
> do you plan to send a PULL req soon? The patch is not really urgent, but
> it would help me move forward with my queue.
>
> Thanks!
> Laszlo
>

Plan to send it next Tuesday. But if you wish, I can send it tomorrow.

Thanks

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH] e1000: eliminate infinite loops on out-of-bounds transfer start
  2016-01-28  5:47         ` Jason Wang
@ 2016-01-28 11:09           ` Laszlo Ersek
  0 siblings, 0 replies; 8+ messages in thread
From: Laszlo Ersek @ 2016-01-28 11:09 UTC (permalink / raw)
  To: Jason Wang
  Cc: Petr Matousek, Michael S. Tsirkin, Stefano Stabellini,
	Michael Tokarev, qemu-devel, Michael Roth, Prasad Pandit

On 01/28/16 06:47, Jason Wang wrote:
> 
> 
> On 01/28/2016 02:35 AM, Laszlo Ersek wrote:
>> Hello Jason,
>>
>> On 01/22/16 07:15, Jason Wang wrote:
>>>
>>> On 01/22/2016 02:11 PM, Michael Tokarev wrote:
>>>> 22.01.2016 06:09, Jason Wang wrote:
>>>>> On 01/19/2016 09:17 PM, Laszlo Ersek wrote:
>>>>>> The start_xmit() and e1000_receive_iov() functions implement DMA transfers
>>>>>> iterating over a set of descriptors that the guest's e1000 driver
>>>>>> prepares:
>>>> ...
>>>>> Applied in my -net.
>>>> This is CVE-2016-1981, btw.
>>>>
>>>> /mjt
>>>>
>>> Add this into commit log.
>> do you plan to send a PULL req soon? The patch is not really urgent, but
>> it would help me move forward with my queue.
>>
>> Thanks!
>> Laszlo
>>
> 
> Plan to send it next Tuesday. But if you wish, I can send it tomorrow.

Next Tuesday is perfectly fine, thank you!
Laszlo

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2016-01-28 11:09 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-19 13:17 [Qemu-devel] [PATCH] e1000: eliminate infinite loops on out-of-bounds transfer start Laszlo Ersek
2016-01-22  3:09 ` Jason Wang
2016-01-22  6:11   ` Michael Tokarev
2016-01-22  6:15     ` Jason Wang
2016-01-22  9:09       ` Laszlo Ersek
2016-01-27 18:35       ` Laszlo Ersek
2016-01-28  5:47         ` Jason Wang
2016-01-28 11:09           ` Laszlo Ersek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).