On Thu, Sep 21, 2017 at 6:10 PM, Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> wrote: > On Thu, Sep 21, 2017 at 05:39:05PM +0200, Andrey Konovalov wrote: >> Hi! >> >> I've got the following report while fuzzing the kernel with syzkaller. >> >> On commit ebb2c2437d8008d46796902ff390653822af6cc4 (Sep 18). >> >> The issue occurs when we iterate over interface altsettings, but I >> don't see the driver doing anything wrong. I might be missing >> something, or this might be an issue in USB core altsettings parsing. > > > Any chance you happen to have the descriptors that you were feeding into > the kernel at this crash? That might help in figuring out what "went > wrong". Here's the data that I feed into dummy_udc: 00 00 00 00 09 02 12 00 01 34 05 80 07 09 04 6e 09 00 08 06 62 00 12 01 05 00 cb f7 71 83 04 00 05 00 ab f6 07 81 08 01 Also attaching a C reproducer (requires dummy_hcd and gadgetfs) and my .config. > > thanks, > > greg k-h
// autogenerated by syzkaller (http://github.com/google/syzkaller) #define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <linux/usb/ch9.h> #include <linux/usb/gadgetfs.h> #include <linux/usbdevice_fs.h> #include <poll.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/inotify.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/types.h> #include <unistd.h> void fail(const char* msg) { printf("%s", msg); exit(EXIT_FAILURE); } void gfs_mkdir() { if (mkdir("/dev/gadget", 0755) != 0 && errno != EEXIST) fail("can't mkdir /dev/gadget\n"); } void gfs_mount() { int attempts = 0; while (mount("none", "/dev/gadget", "gadgetfs", 0, NULL) != 0) { if (++attempts >= 100) fail("can't mount gadgetfs"); usleep(10 * 1000); umount2("/dev/gadget", MNT_FORCE | MNT_DETACH); } } static int gfs_handle_event_setup_get_descriptor(int fd, struct usb_ctrlrequest* setup) { char buffer[128]; int rv; switch (setup->wValue >> 8) { case USB_DT_STRING: buffer[0] = 4; buffer[1] = USB_DT_STRING; if ((setup->wValue & 0x0ff) == 0) { buffer[2] = 0x09; buffer[3] = 0x04; } else { buffer[2] = 0x61; buffer[3] = 0x00; } rv = write(fd, &buffer[0], 4); if (rv != 4) { return -1; } break; default: buffer[0] = 0; rv = write(fd, &buffer[0], 1); return 0; } return 1; } static int gfs_handle_event_setup_set_configuration(int fd, struct usb_ctrlrequest* setup) { int rv = read(fd, &rv, 0); if (rv != 0) { return -1; } return 1; } static int gfs_handle_event_setup_get_interface(int fd, struct usb_ctrlrequest* setup) { char buffer[256]; buffer[0] = 0; int rv = write(fd, &buffer[0], setup->wLength); if (rv < 0) { return -1; } return 1; } static int gfs_handle_event_setup_set_interface(int fd, struct usb_ctrlrequest* setup) { return 0; } static int gfs_handle_event_setup_stall(int fd, struct usb_ctrlrequest* setup) { int status; if (setup->bRequestType & USB_DIR_IN) status = read(fd, &status, 0); else status = write(fd, &status, 0); return 1; } static int gfs_handle_event_setup(int fd, struct usb_ctrlrequest* setup) { int rv; switch (setup->bRequest) { case USB_REQ_GET_DESCRIPTOR: if (setup->bRequestType != USB_DIR_IN) goto stall; rv = gfs_handle_event_setup_get_descriptor(fd, setup); if (rv != 1) goto stall; return 1; case USB_REQ_SET_CONFIGURATION: if (setup->bRequestType != USB_DIR_OUT) goto stall; rv = gfs_handle_event_setup_set_configuration(fd, setup); if (rv != 1) goto stall; return 1; case USB_REQ_GET_INTERFACE: if (setup->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE) || setup->wIndex != 0 || setup->wLength > 1) goto stall; rv = gfs_handle_event_setup_get_interface(fd, setup); if (rv != 1) goto stall; return 1; case USB_REQ_SET_INTERFACE: if (setup->bRequestType != USB_RECIP_INTERFACE || setup->wIndex != 0 || setup->wLength > 1) goto stall; rv = gfs_handle_event_setup_set_interface(fd, setup); if (rv != 1) goto stall; return 1; default: rv = 0; rv = write(fd, &rv, 1); return 0; } stall: gfs_handle_event_setup_stall(fd, setup); return 1; } static int gfs_handle_event(int fd, struct usb_gadgetfs_event* event) { switch (event->type) { case GADGETFS_NOP: break; case GADGETFS_CONNECT: break; case GADGETFS_SETUP: gfs_handle_event_setup(fd, &event->u.setup); break; case GADGETFS_DISCONNECT: break; case GADGETFS_SUSPEND: break; default: break; } return 0; } #define GFS_MAX_EVENTS 5 static int gfs_handle_ep0(int fd) { struct pollfd pfd; struct usb_gadgetfs_event events[GFS_MAX_EVENTS]; pfd.fd = fd; pfd.events = POLLIN | POLLOUT | POLLHUP; int i; for (i = 0; i < 20; i++) { int rv = poll(&pfd, 1, 100); if (rv < 0) { return -1; } if (rv == 0) continue; rv = read(fd, &events[0], sizeof(events)); if (rv < 0) { return -2; } if (rv == 0) continue; unsigned n; for (n = 0; n < rv / sizeof(struct usb_gadgetfs_event); n++) { gfs_handle_event(fd, &events[n]); } } return 0; } struct usb_device_id { uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; uint8_t bIntClass; uint8_t bIntSubClass; uint8_t bIntProtocol; uint8_t bIntNumber; }; static void gfs_patch_usb_descriptor(uintptr_t a1, uintptr_t a2) { struct usb_device_id* id = (struct usb_device_id*)a2; struct usb_interface_descriptor* iface = (struct usb_interface_descriptor*)(a1 + 4 + sizeof(struct usb_config_descriptor)); struct usb_device_descriptor* dev = (struct usb_device_descriptor*)(a1 + 4 + sizeof(struct usb_config_descriptor) + sizeof(struct usb_interface_descriptor) + sizeof(struct usb_endpoint_descriptor) * iface->bNumEndpoints); dev->idVendor = id->idVendor; dev->idProduct = id->idProduct; dev->bcdDevice = id->bcdDevice; dev->bDeviceClass = id->bDeviceClass; dev->bDeviceSubClass = id->bDeviceSubClass; dev->bDeviceProtocol = id->bDeviceProtocol; iface->bInterfaceClass = id->bIntClass; iface->bInterfaceSubClass = id->bIntSubClass; iface->bInterfaceProtocol = id->bIntProtocol; iface->bInterfaceNumber = id->bIntNumber; } static uintptr_t syz_usb_config(uintptr_t a0, uintptr_t a1, uintptr_t a2) { gfs_patch_usb_descriptor(a1, a2); int fd = open("/dev/gadget/dummy_udc", O_RDWR); if (fd < 0) return -1; int64_t length = a0; char* data = (char*)a1; int rv = write(fd, data, length); if (rv < 0) { return -2; } gfs_handle_ep0(fd); return fd; } long r[45]; void loop() { memset(r, -1, sizeof(r)); r[0] = syscall(__NR_mmap, 0x20000000ul, 0x46a000ul, 0x3ul, 0x32ul, 0xfffffffffffffffful, 0x0ul); *(uint32_t*)0x201a5f2b = (uint32_t)0x0; *(uint8_t*)0x201a5f2f = (uint8_t)0x9; *(uint8_t*)0x201a5f30 = (uint8_t)0x2; *(uint16_t*)0x201a5f31 = (uint16_t)0x12; *(uint8_t*)0x201a5f33 = (uint8_t)0x1; *(uint8_t*)0x201a5f34 = (uint8_t)0xfffffffffffff434; *(uint8_t*)0x201a5f35 = (uint8_t)0x5; *(uint8_t*)0x201a5f36 = (uint8_t)0x80; *(uint8_t*)0x201a5f37 = (uint8_t)0x7; *(uint8_t*)0x201a5f38 = (uint8_t)0x9; *(uint8_t*)0x201a5f39 = (uint8_t)0x4; *(uint8_t*)0x201a5f3a = (uint8_t)0x0; *(uint8_t*)0x201a5f3b = (uint8_t)0x9; *(uint8_t*)0x201a5f3c = (uint8_t)0x0; *(uint8_t*)0x201a5f3d = (uint8_t)0x1; *(uint8_t*)0x201a5f3e = (uint8_t)0xa0; *(uint8_t*)0x201a5f3f = (uint8_t)0x1e360; *(uint8_t*)0x201a5f40 = (uint8_t)0x0; *(uint8_t*)0x201a5f41 = (uint8_t)0x12; *(uint8_t*)0x201a5f42 = (uint8_t)0x1; *(uint16_t*)0x201a5f43 = (uint16_t)0x5; *(uint8_t*)0x201a5f45 = (uint8_t)0x4; *(uint8_t*)0x201a5f46 = (uint8_t)0x1; *(uint8_t*)0x201a5f47 = (uint8_t)0x7fff; *(uint8_t*)0x201a5f48 = (uint8_t)0xc583; *(uint16_t*)0x201a5f49 = (uint16_t)0x3; *(uint16_t*)0x201a5f4b = (uint16_t)0x9; *(uint16_t*)0x201a5f4d = (uint16_t)0x80000000; *(uint8_t*)0x201a5f4f = (uint8_t)0x7; *(uint8_t*)0x201a5f50 = (uint8_t)0x81; *(uint8_t*)0x201a5f51 = (uint8_t)0x8; *(uint8_t*)0x201a5f52 = (uint8_t)0x1; *(uint16_t*)0x20356000 = (uint16_t)0x4; *(uint16_t*)0x20356002 = (uint16_t)0x5; *(uint16_t*)0x20356004 = (uint16_t)0xf6ab; *(uint8_t*)0x20356006 = (uint8_t)0x9cb; *(uint8_t*)0x20356007 = (uint8_t)0xfffffffffffffff7; *(uint8_t*)0x20356008 = (uint8_t)0x1c71; *(uint8_t*)0x20356009 = (uint8_t)0x8; *(uint8_t*)0x2035600a = (uint8_t)0x6; *(uint8_t*)0x2035600b = (uint8_t)0x62; *(uint8_t*)0x2035600c = (uint8_t)0xffffffffffff666e; r[43] = syz_usb_config(0x28ul, 0x201a5f2bul, 0x20356000ul); } int main() { gfs_mkdir(); gfs_mount(); loop(); return 0; }
Attachment:
.config
Description: Binary data