Re: usb/gadget: stalls in dummy_timer

From: Andrey Konovalov
Date: Tue Sep 19 2017 - 09:17:22 EST


On Fri, Sep 15, 2017 at 8:57 PM, Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> wrote:
> On Thu, 14 Sep 2017, Andrey Konovalov wrote:
>
>> On Thu, Sep 14, 2017 at 7:49 PM, Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> wrote:
>> > On Thu, 14 Sep 2017, Andrey Konovalov wrote:
>> >
>> >> Looked at this a little more.
>> >>
>> >> dummy_timer() stucks in an infinite loop. It calls
>> >> usb_hcd_giveback_urb(), which in turn calls usbtouch_irq(), which
>> >> calls usb_submit_urb(), which calls dummy_urb_enqueue() and puts urb
>> >> back into dummy urb queue. dummy_timer() then does goto restart, finds
>> >> the urb and calls usb_hcd_giveback_urb() again. And this process goes
>> >> on again and again. It seems that something should either process the
>> >> urb and set urb->status or it should just expire.
>> >
>> > There is some throttling code, but it applies only to bulk transfers.
>> > Probably because the bandwidth limits for other types are slightly
>> > different. However, I don't think we need to worry about this level of
>> > detail, since the driver makes a number of other approximations anyway.
>> >
>> > Try the patch below; it should fix the problem.
>>
>> Hi Alan,
>>
>> Just tried your patch, my reproducer still hangs the kernel until all
>> memory is exhausted.
>>
>> Thanks!
>
> Hmmm. Your reproducer doesn't run on my system. The mmap system call
> fails, perhaps because this laptop has a 32-bit kernel. So I can't
> tell what's going on.
>
> Can you collect a usbmon trace that shows what happens while the
> reproducer runs? If it turns out to be extremely large, just post an
> initial portion of it.

I've attached the usbmon trace. It's actually quite short, probably
due to the fact that the kernel enters infinite loop.

I've also attached a reproducer that should compile on a 32 bit
system, however I haven't tested whether it reproduces the issue.

>
> Alan Stern
>

Attachment: dummy_timer-stall-usbmon
Description: Binary data

// autogenerated by syzkaller (http://github.com/google/syzkaller)

#define _GNU_SOURCE

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadgetfs.h>
#include <poll.h>
#include <sched.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>

void gadgetfs_mkdir()
{
mkdir("/dev/gadget", 0755) != 0;
}

void gadgetfs_mount()
{
while (mount("none", "/dev/gadget", "gadgetfs", 0, NULL) != 0) {
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:
break;
}
return 0;
}

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 0;
}

static int gfs_handle_event_setup(int fd, struct usb_ctrlrequest* setup)
{
switch (setup->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
gfs_handle_event_setup_get_descriptor(fd, setup);
break;
case USB_REQ_SET_CONFIGURATION:
gfs_handle_event_setup_set_configuration(fd, setup);
break;
case USB_REQ_GET_INTERFACE:
break;
case USB_REQ_SET_INTERFACE:
break;
default:
break;
}
return 0;
}

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 1

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 < 15; 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;
gfs_handle_event(fd, &events[0]);
}

return 0;
}

static uintptr_t syz_usb_config(uintptr_t a0, uintptr_t a1)
{
int fd = open("/dev/gadget/dummy_udc", O_RDWR);
if (fd < 0)
return -1;

usleep(30 * 1000);

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[66];

void loop()
{
memset(r, -1, sizeof(r));
r[0] = (long)mmap((void*)0x23000ul, 0x1000ul, 0x3ul, 0x32ul,
0xfffffffful, 0x0ul);
*(uint32_t*)0x23000 = (uint32_t)0x0;
*(uint8_t*)0x23004 = (uint8_t)0x9;
*(uint8_t*)0x23005 = (uint8_t)0x2;
*(uint16_t*)0x23006 = (uint16_t)0x36;
*(uint8_t*)0x23008 = (uint8_t)0x1;
*(uint8_t*)0x23009 = (uint8_t)0x8;
*(uint8_t*)0x2300a = (uint8_t)0x600;
*(uint8_t*)0x2300b = (uint8_t)0x80;
*(uint8_t*)0x2300c = (uint8_t)0x8;
*(uint8_t*)0x2300d = (uint8_t)0x9;
*(uint8_t*)0x2300e = (uint8_t)0x4;
*(uint8_t*)0x2300f = (uint8_t)0x0;
*(uint8_t*)0x23010 = (uint8_t)0x9;
*(uint8_t*)0x23011 = (uint8_t)0x4;
*(uint8_t*)0x23012 = (uint8_t)0xff;
*(uint8_t*)0x23013 = (uint8_t)0x3;
*(uint8_t*)0x23014 = (uint8_t)0x0;
*(uint8_t*)0x23015 = (uint8_t)0x7;
*(uint8_t*)0x23016 = (uint8_t)0x9;
*(uint8_t*)0x23017 = (uint8_t)0x5;
*(uint8_t*)0x23018 = (uint8_t)0x9;
*(uint8_t*)0x23019 = (uint8_t)0x10;
*(uint16_t*)0x2301a = (uint16_t)0x1;
*(uint8_t*)0x2301c = (uint8_t)0x5;
*(uint8_t*)0x2301d = (uint8_t)0xfffff800;
*(uint8_t*)0x2301e = (uint8_t)0x2bba;
*(uint8_t*)0x2301f = (uint8_t)0x9;
*(uint8_t*)0x23020 = (uint8_t)0x5;
*(uint8_t*)0x23021 = (uint8_t)0x898f;
*(uint8_t*)0x23022 = (uint8_t)0x3;
*(uint16_t*)0x23023 = (uint16_t)0x7c113347;
*(uint8_t*)0x23025 = (uint8_t)0x0;
*(uint8_t*)0x23026 = (uint8_t)0xbc;
*(uint8_t*)0x23027 = (uint8_t)0x5;
*(uint8_t*)0x23028 = (uint8_t)0x9;
*(uint8_t*)0x23029 = (uint8_t)0x5;
*(uint8_t*)0x2302a = (uint8_t)0x7;
*(uint8_t*)0x2302b = (uint8_t)0x14;
*(uint16_t*)0x2302c = (uint16_t)0x972f;
*(uint8_t*)0x2302e = (uint8_t)0x1f;
*(uint8_t*)0x2302f = (uint8_t)0x7;
*(uint8_t*)0x23030 = (uint8_t)0x3f;
*(uint8_t*)0x23031 = (uint8_t)0x9;
*(uint8_t*)0x23032 = (uint8_t)0x5;
*(uint8_t*)0x23033 = (uint8_t)0x8;
*(uint8_t*)0x23034 = (uint8_t)0x0;
*(uint16_t*)0x23035 = (uint16_t)0x7;
*(uint8_t*)0x23037 = (uint8_t)0x81;
*(uint8_t*)0x23038 = (uint8_t)0x7ff;
*(uint8_t*)0x23039 = (uint8_t)0x200;
*(uint8_t*)0x2303a = (uint8_t)0x12;
*(uint8_t*)0x2303b = (uint8_t)0x1;
*(uint16_t*)0x2303c = (uint16_t)0xffffffff;
*(uint8_t*)0x2303e = (uint8_t)0x7fd;
*(uint8_t*)0x2303f = (uint8_t)0x4;
*(uint8_t*)0x23040 = (uint8_t)0x3;
*(uint8_t*)0x23041 = (uint8_t)0x1000;
*(uint16_t*)0x23042 = (uint16_t)0x403;
*(uint16_t*)0x23044 = (uint16_t)0x1000f9e9;
*(uint16_t*)0x23046 = (uint16_t)0x4;
*(uint8_t*)0x23048 = (uint8_t)0x4;
*(uint8_t*)0x23049 = (uint8_t)0x8;
*(uint8_t*)0x2304a = (uint8_t)0x7ff;
*(uint8_t*)0x2304b = (uint8_t)0x1;
r[65] = syz_usb_config(0x4cul, 0x23000ul);
}

int main()
{
gadgetfs_mkdir();
gadgetfs_mount();
loop();
return 0;
}