Bug 1326708

Summary: qemu-kvm: Infinite loop in gem_receive()
Product: [Other] Security Response Reporter: Adam Mariš <amaris>
Component: vulnerabilityAssignee: Red Hat Product Security <security-response-team>
Status: CLOSED NOTABUG QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: unspecifiedCC: abaron, ailan, alonbl, aortega, apevec, areis, ayoung, bmcclain, chrisw, dallan, dblechte, drjones, eedri, gklein, gkotton, imammedo, jen, jschluet, knoel, lhh, lpeer, markmc, mgoldboi, michal.skrivanek, mkenneth, mrezanin, mst, pbonzini, ppandit, rbalakri, rbryant, rkrcmar, sclewis, security-response-team, sherold, srevivo, tdecacqu, vkuznets, ykaul, ylavi
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2016-09-21 06:17:45 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Bug Depends On:    
Bug Blocks: 1326713    
Attachments:
Description Flags
Crash report none

Description Adam Mariš 2016-04-13 10:54:40 UTC
An infinite loop vulnerability was found in gem_receive().

Vulnerable code:

static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
...

rxbufsize = ((s->regs[GEM_DMACFG] & GEM_DMACFG_RBUFSZ_M) >> // here set the rxbufsize=0
GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL;
bytes_to_copy = size;

...

DB_PRINT("config bufsize: %d packet size: %ld\n", rxbufsize, size);

while (bytes_to_copy) { // the bytes_to_copy never be changed
/* Do nothing if receive is not enabled. */
if (!gem_can_receive(nc)) {
assert(!first_desc);
return -1;
}

DB_PRINT("copy %d bytes to 0x%x\n", MIN(bytes_to_copy, rxbufsize),
rx_desc_get_buffer(s->rx_desc));

/* Copy packet data to emulated DMA buffer */
cpu_physical_memory_write(rx_desc_get_buffer(s->rx_desc) + rxbuf_offset,
rxbuf_ptr, MIN(bytes_to_copy, rxbufsize));
rxbuf_ptr += MIN(bytes_to_copy, rxbufsize);
bytes_to_copy -= MIN(bytes_to_copy, rxbufsize); // the bytes_to_copy never change


...

/* Descriptor write-back. */
cpu_physical_memory_write(s->rx_desc_addr,
(uint8_t *)s->rx_desc, sizeof(s->rx_desc));

/* Next descriptor */
if (rx_desc_get_wrap(s->rx_desc)) {
DB_PRINT("wrapping RX descriptor list\n");
s->rx_desc_addr = s->regs[GEM_RXQBASE];
} else {
DB_PRINT("incrementing RX descriptor list\n");
s->rx_desc_addr += 8; // provide a lot of rx_desc_addr so let this run correctly
}
gem_get_rx_desc(s);
}

...

return size;
}

By setting rxbufsize to 0 (via setting s->regs[GEM_DMACFG]=0), the while condition bytes_to_copy will never change. Although gem_can_receive() has got check for s->rx_desc, by raising s->rx_desc_addr the statement will be executed everytime, causing an infinite loop.

Comment 1 Adam Mariš 2016-04-13 10:55:01 UTC
Acknowledgments:

Name: Li Qiang (Qihoo 360 Inc.)

Comment 3 Adam Mariš 2016-04-13 10:56:43 UTC
Created attachment 1146825 [details]
Crash report

Comment 4 Prasad Pandit 2016-09-21 06:08:29 UTC
Upstream fix:
-------------
  -> git.qemu.org/?p=qemu.git;a=commit;h=f265ae8c79ce8c194de481e9def1daa3a80dbb96