mirror of https://github.com/proxmox/mirror_qemu
ehci: Verify a queue's ep direction does not change
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>master
parent
51e0c5d029
commit
bbbc39ccac
|
@ -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
|
/* Finish executing and writeback a packet outside of the regular
|
||||||
fetchqh -> fetchqtd -> execute -> writeback cycle */
|
fetchqh -> fetchqtd -> execute -> writeback cycle */
|
||||||
static void ehci_writeback_async_complete_packet(EHCIPacket *p)
|
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);
|
packets = ehci_cancel_queue(q);
|
||||||
q->dev = NULL;
|
q->dev = NULL;
|
||||||
q->qtdaddr = 0;
|
q->qtdaddr = 0;
|
||||||
|
q->last_pid = 0;
|
||||||
return packets;
|
return packets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1368,6 +1382,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
|
||||||
}
|
}
|
||||||
|
|
||||||
p->pid = ehci_get_pid(&p->qtd);
|
p->pid = ehci_get_pid(&p->qtd);
|
||||||
|
p->queue->last_pid = p->pid;
|
||||||
endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
|
endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
|
||||||
ep = usb_ep_get(p->queue->dev, p->pid, endp);
|
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)) {
|
if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
|
||||||
break;
|
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 = ehci_alloc_packet(q);
|
||||||
p->qtdaddr = qtdaddr;
|
p->qtdaddr = qtdaddr;
|
||||||
p->qtd = qtd;
|
p->qtd = qtd;
|
||||||
|
|
|
@ -248,6 +248,7 @@ struct EHCIQueue {
|
||||||
EHCIqh qh; /* copy of current QH (being worked on) */
|
EHCIqh qh; /* copy of current QH (being worked on) */
|
||||||
uint32_t qhaddr; /* address QH read from */
|
uint32_t qhaddr; /* address QH read from */
|
||||||
uint32_t qtdaddr; /* address QTD read from */
|
uint32_t qtdaddr; /* address QTD read from */
|
||||||
|
int last_pid; /* pid of last packet executed */
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
QTAILQ_HEAD(pkts_head, EHCIPacket) packets;
|
QTAILQ_HEAD(pkts_head, EHCIPacket) packets;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue