Patchwork [08/32] ehci: Verify a queue's ep direction does not change

login
register
mail settings
Submitter Gerd Hoffmann
Date Jan. 8, 2013, 1:14 p.m.
Message ID <1357650894-16982-9-git-send-email-kraxel@redhat.com>
Download mbox | patch
Permalink /patch/210383/
State New
Headers show

Comments

Gerd Hoffmann - Jan. 8, 2013, 1:14 p.m.
From: Hans de Goede <hdegoede@redhat.com>

ehci_fill_queue assumes that there is a one on one relationship between an ep
and a qh, this patch adds a check to ensure this.

Note I don't expect this to ever trigger, this is just something I noticed
the guest might do while working on other stuff. The only way this check can
trigger is if a guest mixes in and out qtd-s in a single qh for a non
control ep.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 hw/usb/hcd-ehci.c |   19 +++++++++++++++++++
 hw/usb/hcd-ehci.h |    1 +
 2 files changed, 20 insertions(+), 0 deletions(-)

Patch

diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index dae414a..5d314a0 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -527,6 +527,19 @@  static bool ehci_verify_qtd(EHCIPacket *p, EHCIqtd *qtd)
     }
 }
 
+static bool ehci_verify_pid(EHCIQueue *q, EHCIqtd *qtd)
+{
+    int ep  = get_field(q->qh.epchar, QH_EPCHAR_EP);
+    int pid = ehci_get_pid(qtd);
+
+    /* Note the pid changing is normal for ep 0 (the control ep) */
+    if (q->last_pid && ep != 0 && pid != q->last_pid) {
+        return false;
+    } else {
+        return true;
+    }
+}
+
 /* Finish executing and writeback a packet outside of the regular
    fetchqh -> fetchqtd -> execute -> writeback cycle */
 static void ehci_writeback_async_complete_packet(EHCIPacket *p)
@@ -634,6 +647,7 @@  static int ehci_reset_queue(EHCIQueue *q)
     packets = ehci_cancel_queue(q);
     q->dev = NULL;
     q->qtdaddr = 0;
+    q->last_pid = 0;
     return packets;
 }
 
@@ -1368,6 +1382,7 @@  static int ehci_execute(EHCIPacket *p, const char *action)
     }
 
     p->pid = ehci_get_pid(&p->qtd);
+    p->queue->last_pid = p->pid;
     endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
     ep = usb_ep_get(p->queue->dev, p->pid, endp);
 
@@ -1883,6 +1898,10 @@  static int ehci_fill_queue(EHCIPacket *p)
         if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
             break;
         }
+        if (!ehci_verify_pid(q, &qtd)) {
+            ehci_trace_guest_bug(q->ehci, "guest queued token with wrong pid");
+            break;
+        }
         p = ehci_alloc_packet(q);
         p->qtdaddr = qtdaddr;
         p->qtd = qtd;
diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
index e35144d..14ee3be 100644
--- a/hw/usb/hcd-ehci.h
+++ b/hw/usb/hcd-ehci.h
@@ -248,6 +248,7 @@  struct EHCIQueue {
     EHCIqh qh;             /* copy of current QH (being worked on) */
     uint32_t qhaddr;       /* address QH read from                 */
     uint32_t qtdaddr;      /* address QTD read from                */
+    int last_pid;          /* pid of last packet executed          */
     USBDevice *dev;
     QTAILQ_HEAD(pkts_head, EHCIPacket) packets;
 };