Re: usb/gadget: stalls in dummy_timer

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux