112 lines
4.1 KiB
Diff
112 lines
4.1 KiB
Diff
|
|
From 11d2262ba82c29dd3b6bdfe1e40f57efad550ffc Mon Sep 17 00:00:00 2001
|
||
|
|
From: tangbinzy <tangbin_yewu@cmss.chinamobile.com>
|
||
|
|
Date: Sat, 12 Oct 2024 09:47:17 +0000
|
||
|
|
Subject: [PATCH] hcd-ohci: Drop ohci_service_iso_td() if ed->head &
|
||
|
|
OHCI_DPTR_MASK is zero mainline inclusion commit
|
||
|
|
d8c2e6f2f6d29ccb766197181eb1c65c1d46b3a4 category: bugfix
|
||
|
|
|
||
|
|
---------------------------------------------------------------
|
||
|
|
|
||
|
|
An abort happens in ohci_frame_boundary() when ohci->done is 0 [1].
|
||
|
|
|
||
|
|
``` c
|
||
|
|
static void ohci_frame_boundary(void *opaque)
|
||
|
|
{
|
||
|
|
// ...
|
||
|
|
if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) {
|
||
|
|
if (!ohci->done)
|
||
|
|
abort(); <----------------------------------------- [1]
|
||
|
|
```
|
||
|
|
|
||
|
|
This was reported in https://bugs.launchpad.net/qemu/+bug/1911216/,
|
||
|
|
https://lists.gnu.org/archive/html/qemu-devel/2021-06/msg03613.html, and
|
||
|
|
https://gitlab.com/qemu-project/qemu/-/issues/545. I can still reproduce it with
|
||
|
|
the latest QEMU.
|
||
|
|
|
||
|
|
This happends due to crafted ED with putting ISO_TD at physical address 0.
|
||
|
|
|
||
|
|
Suppose ed->head & OHCI_DPTR_MASK is 0 [2], and we memset 0 to the phyiscal
|
||
|
|
memory from 0 to sizeof(ohci_iso_td). Then, starting_frame [3] and frame_count
|
||
|
|
[4] are both 0. As we can control the value of ohci->frame_number (0 to 0x1f,
|
||
|
|
suppose 1), we then control the value of relative_frame_number to be 1 [6]. The
|
||
|
|
control flow goes to [7] where ohci->done is 0. Have returned from
|
||
|
|
ohci_service_iso_td(), ohci_frame_boundary() will abort() [1].
|
||
|
|
|
||
|
|
``` c
|
||
|
|
static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed)
|
||
|
|
{
|
||
|
|
// ...
|
||
|
|
addr = ed->head & OHCI_DPTR_MASK; // <--------------------- [2]
|
||
|
|
|
||
|
|
if (ohci_read_iso_td(ohci, addr, &iso_td)) { // <-------- [3]
|
||
|
|
// ...
|
||
|
|
|
||
|
|
starting_frame = OHCI_BM(iso_td.flags, TD_SF); // <-------- [4]
|
||
|
|
frame_count = OHCI_BM(iso_td.flags, TD_FC); // <-------- [5]
|
||
|
|
relative_frame_number = USUB(ohci->frame_number, starting_frame);
|
||
|
|
// <-------- [6]
|
||
|
|
if (relative_frame_number < 0) {
|
||
|
|
return 1;
|
||
|
|
} else if (relative_frame_number > frame_count) {
|
||
|
|
// ...
|
||
|
|
ohci->done = addr; // <-------- [7]
|
||
|
|
// ...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
As only (afaik) a guest root user can manipulate ED, TD and the physical memory,
|
||
|
|
this assertion failure is not a security bug.
|
||
|
|
|
||
|
|
The idea to fix this issue is to drop ohci_service_iso_td() if ed->head &
|
||
|
|
OHCI_DPTR_MASK is 0, which is similar to the drop operation for
|
||
|
|
ohci_service_ed_list() when head is 0. Probably, a similar issue is in
|
||
|
|
ohci_service_td(). I drop ohci_service_td() if ed->head & OHCI_DPTR_MASK is 0.
|
||
|
|
|
||
|
|
Fixes: 7bfe577702 ("OHCI USB isochronous transfers support (Arnon Gilboa)")
|
||
|
|
Reported-by: Gaoning Pan <pgn@zju.edu.cn>
|
||
|
|
Reported-by: Alexander Bulekov <alxndr@bu.edu>
|
||
|
|
Reported-by: Qiang Liu <cyruscyliu@gmail.com>
|
||
|
|
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/545
|
||
|
|
Buglink: https://lists.gnu.org/archive/html/qemu-devel/2021-06/msg03613.html
|
||
|
|
Buglink: https://bugs.launchpad.net/qemu/+bug/1911216
|
||
|
|
Signed-off-by: Qiang Liu <cyruscyliu@gmail.com>
|
||
|
|
Message-Id: <20220826051557.119570-1-cyruscyliu@gmail.com>
|
||
|
|
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
|
||
|
|
|
||
|
|
Signed-off-by: tangbinzy <tangbin_yewu@cmss.chinamobile.com>
|
||
|
|
---
|
||
|
|
hw/usb/hcd-ohci.c | 10 ++++++++++
|
||
|
|
1 file changed, 10 insertions(+)
|
||
|
|
|
||
|
|
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
|
||
|
|
index a93d6b2e98..d6f1ae700e 100644
|
||
|
|
--- a/hw/usb/hcd-ohci.c
|
||
|
|
+++ b/hw/usb/hcd-ohci.c
|
||
|
|
@@ -668,6 +668,11 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
||
|
|
|
||
|
|
addr = ed->head & OHCI_DPTR_MASK;
|
||
|
|
|
||
|
|
+ if (addr == 0) {
|
||
|
|
+ ohci_die(ohci);
|
||
|
|
+ return 1;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
if (ohci_read_iso_td(ohci, addr, &iso_td)) {
|
||
|
|
trace_usb_ohci_iso_td_read_failed(addr);
|
||
|
|
ohci_die(ohci);
|
||
|
|
@@ -953,6 +958,11 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||
|
|
int completion;
|
||
|
|
|
||
|
|
addr = ed->head & OHCI_DPTR_MASK;
|
||
|
|
+ if (addr == 0) {
|
||
|
|
+ ohci_die(ohci);
|
||
|
|
+ return 1;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
/* See if this TD has already been submitted to the device. */
|
||
|
|
completion = (addr == ohci->async_td);
|
||
|
|
if (completion && !ohci->async_complete) {
|
||
|
|
--
|
||
|
|
2.41.0.windows.1
|
||
|
|
|