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

login
register
mail settings
Submitter Hans de Goede
Date Dec. 14, 2012, 1:35 p.m.
Message ID <1355492147-5023-9-git-send-email-hdegoede@redhat.com>
Download mbox | patch
Permalink /patch/206493/
State New
Headers show

Comments

Hans de Goede - Dec. 14, 2012, 1:35 p.m.
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>
---
 hw/usb/hcd-ehci.c | 19 +++++++++++++++++++
 hw/usb/hcd-ehci.h |  1 +
 2 files changed, 20 insertions(+)

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 772870b..3c888a6 100644
--- a/hw/usb/hcd-ehci.h
+++ b/hw/usb/hcd-ehci.h
@@ -246,6 +246,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;
 };