summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGerd Hoffmann2018-11-26 11:08:36 +0100
committerGerd Hoffmann2018-12-10 15:30:18 +0100
commitb7d3a7e1a8830af78e71952e82f186b12b70ff1f (patch)
tree1c85bcb28261739dbc9ae3b16e9dac184ac98aba
parentusb-host: reset and close libusb_device_handle before qemu exit (diff)
downloadqemu-b7d3a7e1a8830af78e71952e82f186b12b70ff1f.tar.gz
qemu-b7d3a7e1a8830af78e71952e82f186b12b70ff1f.tar.xz
qemu-b7d3a7e1a8830af78e71952e82f186b12b70ff1f.zip
ehci: fix fetch qtd race
The token field contains the (guest-filled) state of the qtd, which indicates whenever the other fields are valid or not. So make sure we read the token first, otherwise we may end up with an stale next pointer: (1) ehci reads next (2) guest writes next (3) guest writes token (4) ehci reads token (5) ehci operates with stale next. Typical effect is that qemu doesn't notice that the guest appends new qtds to the end of the queue. Looks like the usb device stopped responding. Linux can recover from that, but leaves a message in the kernel log that it did reset the usb device in question. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Message-id: 20181126100836.8805-1-kraxel@redhat.com
-rw-r--r--hw/usb/hcd-ehci.c12
1 files changed, 10 insertions, 2 deletions
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index e5acfc5ba5..8d44d483df 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -1783,9 +1783,17 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
EHCIqtd qtd;
EHCIPacket *p;
int again = 1;
+ uint32_t addr;
- if (get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
- sizeof(EHCIqtd) >> 2) < 0) {
+ addr = NLPTR_GET(q->qtdaddr);
+ if (get_dwords(q->ehci, addr + 8, &qtd.token, 1) < 0) {
+ return 0;
+ }
+ barrier();
+ if (get_dwords(q->ehci, addr + 0, &qtd.next, 1) < 0 ||
+ get_dwords(q->ehci, addr + 4, &qtd.altnext, 1) < 0 ||
+ get_dwords(q->ehci, addr + 12, qtd.bufptr,
+ ARRAY_SIZE(qtd.bufptr)) < 0) {
return 0;
}
ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);