diff options
Diffstat (limited to 'results/classifier/zero-shot/105/other/1180')
| -rw-r--r-- | results/classifier/zero-shot/105/other/1180 | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/results/classifier/zero-shot/105/other/1180 b/results/classifier/zero-shot/105/other/1180 new file mode 100644 index 000000000..d0a4bca79 --- /dev/null +++ b/results/classifier/zero-shot/105/other/1180 @@ -0,0 +1,179 @@ +mistranslation: 0.950 +semantic: 0.949 +other: 0.948 +vnc: 0.939 +graphic: 0.939 +instruction: 0.939 +assembly: 0.937 +KVM: 0.933 +device: 0.928 +socket: 0.925 +network: 0.923 +boot: 0.899 + +Assertion failure in usb_cancel_packet() +Description of problem: +When I ran hcd-ohci with dev-storage, I found an assertion failure in +usb_cancel_packet() [1] due to p->state == USB_PACKET_COMPLETE. This is due to +the inconsistency when resetting device. + +``` c +static inline bool usb_packet_is_inflight(USBPacket *p) +{ + return (p->state == USB_PACKET_QUEUED || + p->state == USB_PACKET_ASYNC); +} + +void usb_cancel_packet(USBPacket * p) +{ + bool callback = (p->state == USB_PACKET_ASYNC); + assert(usb_packet_is_inflight(p)); // <------------------------------- [1] + usb_packet_set_state(p, USB_PACKET_CANCELED); + QTAILQ_REMOVE(&p->ep->queue, p, queue); + if (callback) { + usb_device_cancel_packet(p->ep->dev, p); + } +} +``` +Steps to reproduce: +Step 1: download the prepared rootfs and the image. + +https://drive.google.com/file/d/1B95zWWcomvZt1wms31Ddc9Xwlq-bfqhq/view?usp=sharing + +https://drive.google.com/file/d/1pxFzn49MKYmMMIIsaL9aUkzebRSYfq3J/view?usp=sharing + +Step 2: run the following script. + +``` bash +QEMU_PATH=../../../qemu/build/qemu-system-x86_64 +KERNEL_PATH=./bzImage +ROOTFS_PATH=./rootfs.ext2 +$QEMU_PATH \ + -M q35 -m 1G \ + -kernel $KERNEL_PATH \ + -drive file=$ROOTFS_PATH,if=virtio,format=raw \ + -append "root=/dev/vda console=ttyS0" \ + -net nic,model=virtio -net user \ + -usb \ + -device pci-ohci,num-ports=6 \ + -drive file=null-co://,if=none,format=raw,id=disk0 \ + -device usb-storage,port=1,drive=disk0 \ + -nographic +``` + +Step 3: with spawned shell (the user is root and the password is empty), run +`ohci-03`. +Additional information: +1 With crafted ED and TD, we can have the ohci->usb_packet's status to be +USB_RET_ASYNC [5]. And thus ohci->async_td is not NULL anymore [2]. + +``` +ed0 = { flags = 0x685f0900, tail = 0x0, head = &td0, next = 0 } + +td0 = { flags = 0x0, cbp = 0x1b8ffc, next = 0, be = 0x1b901a } +# data from cbp to be +55 53 42 43 00 00 00 00 00 00 00 00 00 00 00 03 USBC............ +e8 56 20 40 e8 56 20 40 e8 56 20 40 e8 56 20 + +ed1 = { flags = 0x08303080, tail = 0x0, head = &td1, next = 0 } + +td1 = { flags = 0x90000000, cbp = 0x19affc, next = 0, be = 0x19b01a } +# data from cbp to be +55 53 42 43 00 00 00 00 00 00 00 00 00 00 00 03 USBC............ +e8 56 20 40 e8 56 20 40 e8 56 20 40 e8 56 20 +``` + +``` c +static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) +{ + // ... + usb_handle_packet(dev, &ohci->usb_packet); // <------------------- [4] + if (ohci->usb_packet.status == USB_RET_ASYNC) { + usb_device_flush_ep_queue(dev, ep); + ohci->async_td = addr; // <----------------------------------- [2] + return 1; + } +``` + +At the same time, the dev-storage will ref the current usb_packet +(ohci->usb_packet) [4][3]. + +``` +static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) { + // ... + s->packet = p; // <----------------------------------------------- [3] + p->status = USB_RET_ASYNC; // <----------------------------------- [5] + // ... +} +``` + +2 We can first issue `MMIO_WRITE, 0xe0000054, 0x4, 0x4e33b4bf` to reset +the dev-storage device. This will mark the state of ohci->usb_packet to +USB_PACKET_COMPLETE and clear s->packet. + +``` +ohci_mem_write + ohci_port_set_status + usb_device_reset + usb_device_handle_reset + usb_msd_handle_reset + usb_msd_packet_complete + usb_packet_complete +``` + +3 We can then issue `MMIO_WRITE, 0xe0000004, 0x4, 0x3d8d323a` to reset the +roothub and this will invoke ohci_stop_endpoints() where usb_cancel_packet() +is invoked and thus [1] fails as the state of ohci->usb_packet has been changed +to USB_PACKET_COMPLETE. + +``` +ohci_set_ctl + ohci_roothub_reset + ohci_stop_endpoints + if (ohci->async_td != NULL) usb_cancel_packet(&ohci->usb_packet); + assert(usb_packet_is_inflight(p)); // boom +``` + +The above callstack are simplified. The complete callstack is in the following. + +``` +ohci_set_ctl + ohci_roothub_reset + usb_port_reset + usb_detach + ohci_detach + ohci_child_detach // <-------------------------------- [8] + usb_device_reset // <----------------------------------------- [6] + usb_device_handle_reset + usb_msd_handle_reset + usb_msd_packet_complete + usb_packet_complete + ohci_stop_endpoints // <------------------------------------------ [7] + if (ohci->async_td != NULL) usb_cancel_packet(&ohci->usb_packet); + assert(usb_packet_is_inflight(p)); // boom +``` + +Interestingly, in ohci_roothub_reset(), usb_device_reset() is also invoked [6] +just like what in step 2. I adjusted my PoC by removing step 2. However, I +cannot reproduce this assertion failure. Therefore, there is something different +bewteen [6] and step 2. + +Then, I found at [8], ohci_child_detach() cancels the ohci->usb_packet and reset +ohci->async_td. With step 2, as the status of the ohci->usb_packet has changed +to USB_PACKET_COMPLETE, usb_cancel_packet() will not be invoked. Without step 2, +as the status of the ohci->usb_packet is still USB_PACKET_ASYNC, +usb_cancel_packet() will be invoked and thus everything goes fine. + +``` +static void ohci_child_detach(USBPort *port1, USBDevice *dev) +{ + OHCIState *ohci = port1->opaque; + + if (ohci->async_td && + usb_packet_is_inflight(&ohci->usb_packet) && + ohci->usb_packet.ep->dev == dev) { + usb_cancel_packet(&ohci->usb_packet); + ohci->async_td = 0; + } +} +``` |