Re: usb/storage/uas: slab-out-of-bounds in uas_probe

From: Andrey Konovalov
Date: Thu Sep 21 2017 - 12:24:34 EST


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