KASAN: use-after-free Read in service_outstanding_interrupt

31 views
Skip to first unread message

syzbot

unread,
Aug 7, 2020, 8:19:17 AM8/7/20
to andre...@google.com, gre...@linuxfoundation.org, gusta...@kernel.org, ingr...@epigenesys.com, linux-...@vger.kernel.org, linu...@vger.kernel.org, syzkall...@googlegroups.com
Hello,

syzbot found the following issue on:

HEAD commit: 7b4ea945 Revert "x86/mm/64: Do not sync vmalloc/ioremap ma..
git tree: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
console output: https://syzkaller.appspot.com/x/log.txt?x=108adf32900000
kernel config: https://syzkaller.appspot.com/x/.config?x=72a84c46d0c668c
dashboard link: https://syzkaller.appspot.com/bug?extid=9e04e2df4a32fb661daf
compiler: gcc (GCC) 10.1.0-syz 20200507

Unfortunately, I don't have any reproducer for this issue yet.

IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+9e04e2...@syzkaller.appspotmail.com

==================================================================
BUG: KASAN: use-after-free in usb_submit_urb+0x10c4/0x13e0 drivers/usb/core/urb.c:368
Read of size 4 at addr ffff8881caa52018 by task syz-executor.3/13922

CPU: 1 PID: 13922 Comm: syz-executor.3 Not tainted 5.8.0-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0xf6/0x16e lib/dump_stack.c:118
print_address_description.constprop.0+0x1a/0x210 mm/kasan/report.c:383
__kasan_report mm/kasan/report.c:513 [inline]
kasan_report.cold+0x37/0x7c mm/kasan/report.c:530
usb_submit_urb+0x10c4/0x13e0 drivers/usb/core/urb.c:368
service_outstanding_interrupt.part.0+0x5f/0xa0 drivers/usb/class/cdc-wdm.c:463
service_outstanding_interrupt drivers/usb/class/cdc-wdm.c:458 [inline]
wdm_read+0x9a0/0xbd0 drivers/usb/class/cdc-wdm.c:576
vfs_read+0x1df/0x520 fs/read_write.c:479
ksys_read+0x12d/0x250 fs/read_write.c:607
do_syscall_64+0x2d/0x40 arch/x86/entry/common.c:46
entry_SYSCALL_64_after_hwframe+0x44/0xa9
RIP: 0033:0x45ce79
Code: 2d b6 fb ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 fb b5 fb ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007f8d2099fc78 EFLAGS: 00000246 ORIG_RAX: 0000000000000000
RAX: ffffffffffffffda RBX: 00000000000252c0 RCX: 000000000045ce79
RDX: 00000000000000f2 RSI: 0000000020000100 RDI: 0000000000000003
RBP: 000000000118bf60 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 000000000118bf2c
R13: 00007ffd66f595ff R14: 00007f8d209a09c0 R15: 000000000118bf2c

Allocated by task 4524:
save_stack+0x1b/0x40 mm/kasan/common.c:48
set_track mm/kasan/common.c:56 [inline]
__kasan_kmalloc.constprop.0+0xc2/0xd0 mm/kasan/common.c:494
kmalloc include/linux/slab.h:555 [inline]
kzalloc include/linux/slab.h:669 [inline]
usb_alloc_dev+0x51/0xf67 drivers/usb/core/usb.c:582
hub_port_connect drivers/usb/core/hub.c:5114 [inline]
hub_port_connect_change drivers/usb/core/hub.c:5348 [inline]
port_event drivers/usb/core/hub.c:5494 [inline]
hub_event+0x1dff/0x4390 drivers/usb/core/hub.c:5576
process_one_work+0x94c/0x15f0 kernel/workqueue.c:2269
worker_thread+0x64c/0x1120 kernel/workqueue.c:2415
kthread+0x392/0x470 kernel/kthread.c:292
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:294

Freed by task 5477:
save_stack+0x1b/0x40 mm/kasan/common.c:48
set_track mm/kasan/common.c:56 [inline]
kasan_set_free_info mm/kasan/common.c:316 [inline]
__kasan_slab_free+0x116/0x160 mm/kasan/common.c:455
slab_free_hook mm/slub.c:1474 [inline]
slab_free_freelist_hook+0x53/0x140 mm/slub.c:1507
slab_free mm/slub.c:3072 [inline]
kfree+0xbc/0x2c0 mm/slub.c:4052
device_release+0x71/0x200 drivers/base/core.c:1800
kobject_cleanup lib/kobject.c:704 [inline]
kobject_release lib/kobject.c:735 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x1c8/0x540 lib/kobject.c:752
put_device+0x1b/0x30 drivers/base/core.c:3029
hub_port_connect drivers/usb/core/hub.c:5059 [inline]
hub_port_connect_change drivers/usb/core/hub.c:5348 [inline]
port_event drivers/usb/core/hub.c:5494 [inline]
hub_event+0x1c93/0x4390 drivers/usb/core/hub.c:5576
process_one_work+0x94c/0x15f0 kernel/workqueue.c:2269
worker_thread+0x64c/0x1120 kernel/workqueue.c:2415
kthread+0x392/0x470 kernel/kthread.c:292
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:294

The buggy address belongs to the object at ffff8881caa52000
which belongs to the cache kmalloc-2k of size 2048
The buggy address is located 24 bytes inside of
2048-byte region [ffff8881caa52000, ffff8881caa52800)
The buggy address belongs to the page:
page:ffffea00072a9400 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 head:ffffea00072a9400 order:3 compound_mapcount:0 compound_pincount:0
flags: 0x200000000010200(slab|head)
raw: 0200000000010200 0000000000000000 0000000100000001 ffff8881da00c000
raw: 0000000000000000 0000000000080008 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected

Memory state around the buggy address:
ffff8881caa51f00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff8881caa51f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff8881caa52000: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff8881caa52080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff8881caa52100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================


---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzk...@googlegroups.com.

syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.

syzbot

unread,
Dec 17, 2020, 10:21:12 PM12/17/20
to andre...@google.com, gre...@linuxfoundation.org, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org, linux-...@vger.kernel.org, linu...@vger.kernel.org, one...@suse.com, penguin...@i-love.sakura.ne.jp, syzkall...@googlegroups.com
syzbot has found a reproducer for the following issue on:

HEAD commit: 5e60366d Merge tag 'fallthrough-fixes-clang-5.11-rc1' of g..
console output: https://syzkaller.appspot.com/x/log.txt?x=12c5b623500000
kernel config: https://syzkaller.appspot.com/x/.config?x=5cea7506b7139727
dashboard link: https://syzkaller.appspot.com/bug?extid=9e04e2df4a32fb661daf
compiler: gcc (GCC) 10.1.0-syz 20200507
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=175adf07500000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=1672680f500000

IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+9e04e2...@syzkaller.appspotmail.com

==================================================================
BUG: KASAN: use-after-free in usb_submit_urb+0x1210/0x1560 drivers/usb/core/urb.c:383
Read of size 4 at addr ffff888101d21018 by task syz-executor166/4405

CPU: 0 PID: 4405 Comm: syz-executor166 Not tainted 5.10.0-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:79 [inline]
dump_stack+0x107/0x163 lib/dump_stack.c:120
print_address_description.constprop.0.cold+0xae/0x4c8 mm/kasan/report.c:385
__kasan_report mm/kasan/report.c:545 [inline]
kasan_report.cold+0x1f/0x37 mm/kasan/report.c:562
usb_submit_urb+0x1210/0x1560 drivers/usb/core/urb.c:383
service_outstanding_interrupt.part.0+0x5f/0xa0 drivers/usb/class/cdc-wdm.c:470
service_outstanding_interrupt drivers/usb/class/cdc-wdm.c:465 [inline]
wdm_read+0x9a0/0xbd0 drivers/usb/class/cdc-wdm.c:583
vfs_read+0x1b5/0x570 fs/read_write.c:494
ksys_read+0x12d/0x250 fs/read_write.c:634
do_syscall_64+0x2d/0x40 arch/x86/entry/common.c:46
entry_SYSCALL_64_after_hwframe+0x44/0xa9
RIP: 0033:0x44b529
Code: e8 bc b4 02 00 48 83 c4 18 c3 0f 1f 80 00 00 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 8b cb fb ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007f2dfcb6ed98 EFLAGS: 00000246 ORIG_RAX: 0000000000000000
RAX: ffffffffffffffda RBX: 00000000006dcc38 RCX: 000000000044b529
RDX: 0000000000001000 RSI: 0000000020001000 RDI: 0000000000000004
RBP: 00000000006dcc30 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00000000006dcc3c
R13: 0142006002090100 R14: 04010040a4157d25 R15: 4000000200000112

Allocated by task 2632:
kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48
kasan_set_track mm/kasan/common.c:56 [inline]
__kasan_kmalloc.constprop.0+0xc2/0xd0 mm/kasan/common.c:461
kmalloc include/linux/slab.h:552 [inline]
kzalloc include/linux/slab.h:682 [inline]
usb_alloc_dev+0x51/0xef0 drivers/usb/core/usb.c:582
hub_port_connect drivers/usb/core/hub.c:5129 [inline]
hub_port_connect_change drivers/usb/core/hub.c:5363 [inline]
port_event drivers/usb/core/hub.c:5509 [inline]
hub_event+0x1def/0x42d0 drivers/usb/core/hub.c:5591
process_one_work+0x98d/0x15c0 kernel/workqueue.c:2275
worker_thread+0x64c/0x1120 kernel/workqueue.c:2421
kthread+0x38c/0x460 kernel/kthread.c:292
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:296

Freed by task 2181:
kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48
kasan_set_track+0x1c/0x30 mm/kasan/common.c:56
kasan_set_free_info+0x1b/0x30 mm/kasan/generic.c:352
__kasan_slab_free+0x102/0x140 mm/kasan/common.c:422
slab_free_hook mm/slub.c:1544 [inline]
slab_free_freelist_hook+0x5d/0x150 mm/slub.c:1577
slab_free mm/slub.c:3140 [inline]
kfree+0xdb/0x3a0 mm/slub.c:4122
device_release+0x9f/0x240 drivers/base/core.c:1962
kobject_cleanup lib/kobject.c:705 [inline]
kobject_release lib/kobject.c:736 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x1c8/0x540 lib/kobject.c:753
put_device+0x1b/0x30 drivers/base/core.c:3190
hub_port_connect drivers/usb/core/hub.c:5074 [inline]
hub_port_connect_change drivers/usb/core/hub.c:5363 [inline]
port_event drivers/usb/core/hub.c:5509 [inline]
hub_event+0x1c8a/0x42d0 drivers/usb/core/hub.c:5591
process_one_work+0x98d/0x15c0 kernel/workqueue.c:2275
worker_thread+0x64c/0x1120 kernel/workqueue.c:2421
kthread+0x38c/0x460 kernel/kthread.c:292
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:296

The buggy address belongs to the object at ffff888101d21000
which belongs to the cache kmalloc-2k of size 2048
The buggy address is located 24 bytes inside of
2048-byte region [ffff888101d21000, ffff888101d21800)
The buggy address belongs to the page:
page:0000000019761127 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x101d20
head:0000000019761127 order:3 compound_mapcount:0 compound_pincount:0
flags: 0x200000000010200(slab|head)
raw: 0200000000010200 dead000000000100 dead000000000122 ffff888100042000
raw: 0000000000000000 0000000080080008 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected

Memory state around the buggy address:
ffff888101d20f00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888101d20f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888101d21000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888101d21080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888101d21100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================

Hillf Danton

unread,
Dec 18, 2020, 3:21:36 AM12/18/20
to syzbot, andre...@google.com, gre...@linuxfoundation.org, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org, linux-...@vger.kernel.org, linu...@vger.kernel.org, syzkall...@googlegroups.com
On Thu, 17 Dec 2020 19:21:10 -0800
Bail out without urb submitted if wdm device is disconnecting.

--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -474,18 +474,25 @@ out:
return rv;
}

-static ssize_t wdm_read
-(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+static ssize_t wdm_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
{
int rv, cntr;
int i = 0;
struct wdm_device *desc = file->private_data;

+ if (test_bit(WDM_DISCONNECTING, &desc->flags))
+ return -ENODEV;

rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
if (rv < 0)
return -ERESTARTSYS;

+ if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
+ rv = -ENODEV;
+ goto err;
+ }
+
cntr = READ_ONCE(desc->length);
if (cntr == 0) {
desc->read = 0;

Greg KH

unread,
Dec 18, 2020, 3:28:22 AM12/18/20
to Hillf Danton, syzbot, andre...@google.com, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org, linux-...@vger.kernel.org, linu...@vger.kernel.org, syzkall...@googlegroups.com
Why test before the lock if you test right after the lock?

and what happens if the bit is set right _after_ you test this?

thanks,

greg k-h

Hillf Danton

unread,
Dec 18, 2020, 5:01:56 AM12/18/20
to Greg KH, syzbot, andre...@google.com, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org, linux-...@vger.kernel.org, linu...@vger.kernel.org, syzkall...@googlegroups.com
Any test is supposed to make sense.
>
>and what happens if the bit is set right _after_ you test this?

Your question explains the above double test.

If we get desc->rlock and fail to test WDM_DISCONNECTING in the
read path then we are safe because the same lock is acquired also
in the disconnect path.

Greg KH

unread,
Dec 18, 2020, 5:32:51 AM12/18/20
to Hillf Danton, syzbot, andre...@google.com, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org, linux-...@vger.kernel.org, linu...@vger.kernel.org, syzkall...@googlegroups.com
I do not understand.

I am trying to say, why do you even have the test outside of the lock as
that makes no sense?

> >and what happens if the bit is set right _after_ you test this?
>
> Your question explains the above double test.

No it doesn't :)

> If we get desc->rlock and fail to test WDM_DISCONNECTING in the
> read path then we are safe because the same lock is acquired also
> in the disconnect path.

Then only check the bit _when_ the lock is held. Otherwise it is a
pointless check.

thanks,

greg k-h

Tetsuo Handa

unread,
Dec 18, 2020, 9:03:59 AM12/18/20
to one...@suse.com, syzbot, linu...@vger.kernel.org, syzkall...@googlegroups.com, andre...@google.com, gre...@linuxfoundation.org, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org
Hello, Oliver.

I guess that this is caused by not checking WDM_DISCONNECTING/WDM_RESETTING
before usb_submit_urb(), and below diff seems to avoid use-after-free for
the C reproducer syzbot has found.

Since service_outstanding_interrupt() is called from service_interrupt_work()
without holding desc->rlock, it is possible that kill_urbs() is called by
wdm_disconnect() when service_outstanding_interrupt() is preempted between
spin_unlock_irq(&desc->iuspin) and usb_submit_urb(), isn't it?
Therefore, we also need to make sure that kill_urbs(desc) is called after
cancel_work_sync(&desc->service_outs_intr) which waits for
service_interrupt_work() to return completed, don't we?

diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 02d0cfd23bb2..508b1c3f8b73 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -465,13 +465,23 @@ static int service_outstanding_interrupt(struct wdm_device *desc)
if (!desc->resp_count || !--desc->resp_count)
goto out;

+ if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
+ rv = -ENODEV;
+ goto out;
+ }
+ if (test_bit(WDM_RESETTING, &desc->flags)) {
+ rv = -EIO;
+ goto out;
+ }
+
set_bit(WDM_RESPONDING, &desc->flags);
spin_unlock_irq(&desc->iuspin);
rv = usb_submit_urb(desc->response, GFP_KERNEL);
spin_lock_irq(&desc->iuspin);
if (rv) {
- dev_err(&desc->intf->dev,
- "usb_submit_urb failed with result %d\n", rv);
+ if (!test_bit(WDM_DISCONNECTING, &desc->flags))
+ dev_err(&desc->intf->dev,
+ "usb_submit_urb failed with result %d\n", rv);

/* make sure the next notification trigger a submit */
clear_bit(WDM_RESPONDING, &desc->flags);
@@ -1027,9 +1037,9 @@ static void wdm_disconnect(struct usb_interface *intf)
wake_up_all(&desc->wait);
mutex_lock(&desc->rlock);
mutex_lock(&desc->wlock);
- kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
cancel_work_sync(&desc->service_outs_intr);
+ kill_urbs(desc);
mutex_unlock(&desc->wlock);
mutex_unlock(&desc->rlock);


Tetsuo Handa

unread,
Dec 19, 2020, 10:26:52 AM12/19/20
to one...@suse.com, syzbot, linu...@vger.kernel.org, syzkall...@googlegroups.com, andre...@google.com, gre...@linuxfoundation.org, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org
syzbot is reporting UAF at usb_submit_urb() [1], for
service_outstanding_interrupt() is not checking WDM_DISCONNECTING
before calling usb_submit_urb(). Close the race by doing same checks
wdm_read() does upon retry.

Also, while wdm_read() checks WDM_DISCONNECTING with desc->rlock held,
service_interrupt_work() does not hold desc->rlock. Thus, it is possible
that usb_submit_urb() is called from service_outstanding_interrupt() from
service_interrupt_work() after WDM_DISCONNECTING was set and kill_urbs()
from wdm_disconnect() completed. Thus, move kill_urbs() in
wdm_disconnect() to after cancel_work_sync() (which makes sure that
service_interrupt_work() is no longer running) completed.

Although it seems to be safe to dereference desc->intf->dev in
service_outstanding_interrupt() even if WDM_DISCONNECTING was already set
because desc->rlock or cancel_work_sync() prevents wdm_disconnect() from
reaching list_del() before service_outstanding_interrupt() completes,
let's not emit error message if WDM_DISCONNECTING is set by
wdm_disconnect() while usb_submit_urb() is in progress.

[1] https://syzkaller.appspot.com/bug?extid=9e04e2df4a32fb661daf

Reported-by: syzbot <syzbot+9e04e2...@syzkaller.appspotmail.com>
Signed-off-by: Tetsuo Handa <penguin...@I-love.SAKURA.ne.jp>
---
drivers/usb/class/cdc-wdm.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
--
2.25.1


Oliver Neukum

unread,
Dec 28, 2020, 9:44:30 AM12/28/20
to Tetsuo Handa, syzbot, linu...@vger.kernel.org, syzkall...@googlegroups.com, andre...@google.com, gre...@linuxfoundation.org, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org
Am Sonntag, den 20.12.2020, 00:25 +0900 schrieb Tetsuo Handa:
> syzbot is reporting UAF at usb_submit_urb() [1], for
> service_outstanding_interrupt() is not checking WDM_DISCONNECTING
> before calling usb_submit_urb(). Close the race by doing same checks
> wdm_read() does upon retry.

But wdm_read() does them with proper locking.

> Also, while wdm_read() checks WDM_DISCONNECTING with desc->rlock held,
> service_interrupt_work() does not hold desc->rlock. Thus, it is possible
> that usb_submit_urb() is called from service_outstanding_interrupt() from
> service_interrupt_work() after WDM_DISCONNECTING was set and kill_urbs()
> from wdm_disconnect() completed. Thus, move kill_urbs() in
> wdm_disconnect() to after cancel_work_sync() (which makes sure that
> service_interrupt_work() is no longer running) completed.

That seems to be the right approach. You must prevent this helper
from being called in the first place.

Regards
Oliver


Oliver Neukum

unread,
Jan 4, 2021, 11:28:28 AM1/4/21
to syzbot, andre...@google.com, gre...@linuxfoundation.org, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org, linux-...@vger.kernel.org, linu...@vger.kernel.org, penguin...@i-love.sakura.ne.jp, syzkall...@googlegroups.com
Am Donnerstag, den 17.12.2020, 19:21 -0800 schrieb syzbot:
> syzbot has found a reproducer for the following issue on:
>
> HEAD commit: 5e60366d Merge tag 'fallthrough-fixes-clang-5.11-rc1' of g..
> git tree: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
> console output: https://syzkaller.appspot.com/x/log.txt?x=12c5b623500000
> kernel config: https://syzkaller.appspot.com/x/.config?x=5cea7506b7139727
> dashboard link: https://syzkaller.appspot.com/bug?extid=9e04e2df4a32fb661daf
> compiler: gcc (GCC) 10.1.0-syz 20200507
> syz repro: https://syzkaller.appspot.com/x/repro.syz?x=175adf07500000
> C reproducer: https://syzkaller.appspot.com/x/repro.c?x=1672680f500000
>
> IMPORTANT: if you fix the issue, please add the following tag to the commit:
> Reported-by: syzbot+9e04e2...@syzkaller.appspotmail.com

#syz test: https://github.com/google/kasan.git 5e60366d

From f51e3c5a202f3abc805edd64b21a68d29dd9d60e Mon Sep 17 00:00:00 2001
From: Oliver Neukum <one...@suse.com>
Date: Mon, 4 Jan 2021 17:26:33 +0100
Subject: [PATCH] cdc-wdm: poison URBs upon disconnect

We have a chicken and egg issue between interrupt and work.
This should break the cycle.

Signed-off-by: Oliver Neukum <one...@suse.com>
---
drivers/usb/class/cdc-wdm.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 02d0cfd23bb2..14eddda35280 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -324,9 +324,9 @@ static void wdm_int_callback(struct urb *urb)
static void kill_urbs(struct wdm_device *desc)
{
/* the order here is essential */
- usb_kill_urb(desc->command);
- usb_kill_urb(desc->validity);
- usb_kill_urb(desc->response);
+ usb_poison_urb(desc->command);
+ usb_poison_urb(desc->validity);
+ usb_poison_urb(desc->response);
}

static void free_urbs(struct wdm_device *desc)
--
2.26.2


syzbot

unread,
Jan 4, 2021, 11:44:08 AM1/4/21
to andre...@google.com, gre...@linuxfoundation.org, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org, linux-...@vger.kernel.org, linu...@vger.kernel.org, penguin...@i-love.sakura.ne.jp, syzkall...@googlegroups.com
Hello,

syzbot has tested the proposed patch but the reproducer is still triggering an issue:
KASAN: use-after-free Read in service_outstanding_interrupt

==================================================================
BUG: KASAN: use-after-free in usb_submit_urb+0x1210/0x1560 drivers/usb/core/urb.c:383
Read of size 4 at addr ffff888113e9f018 by task syz-executor.0/7799

CPU: 1 PID: 7799 Comm: syz-executor.0 Not tainted 5.10.0-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:79 [inline]
dump_stack+0x107/0x163 lib/dump_stack.c:120
print_address_description.constprop.0.cold+0xae/0x4c8 mm/kasan/report.c:385
__kasan_report mm/kasan/report.c:545 [inline]
kasan_report.cold+0x1f/0x37 mm/kasan/report.c:562
usb_submit_urb+0x1210/0x1560 drivers/usb/core/urb.c:383
service_outstanding_interrupt.part.0+0x5f/0xa0 drivers/usb/class/cdc-wdm.c:470
service_outstanding_interrupt drivers/usb/class/cdc-wdm.c:465 [inline]
wdm_read+0x9a0/0xbd0 drivers/usb/class/cdc-wdm.c:583
vfs_read+0x1b5/0x570 fs/read_write.c:494
ksys_read+0x12d/0x250 fs/read_write.c:634
do_syscall_64+0x2d/0x40 arch/x86/entry/common.c:46
entry_SYSCALL_64_after_hwframe+0x44/0xa9
RIP: 0033:0x45e149
Code: 0d b4 fb ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 db b3 fb ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007fcce8099c68 EFLAGS: 00000246 ORIG_RAX: 0000000000000000
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 000000000045e149
RDX: 0000000000001000 RSI: 0000000020001000 RDI: 0000000000000004
RBP: 000000000119c068 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 000000000119c034
R13: 00007fffc13967df R14: 00007fcce809a9c0 R15: 000000000119c034

Allocated by task 17:
kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48
kasan_set_track mm/kasan/common.c:56 [inline]
__kasan_kmalloc.constprop.0+0xc2/0xd0 mm/kasan/common.c:461
kmalloc include/linux/slab.h:552 [inline]
kzalloc include/linux/slab.h:682 [inline]
usb_alloc_dev+0x51/0xef0 drivers/usb/core/usb.c:582
hub_port_connect drivers/usb/core/hub.c:5129 [inline]
hub_port_connect_change drivers/usb/core/hub.c:5363 [inline]
port_event drivers/usb/core/hub.c:5509 [inline]
hub_event+0x1def/0x42d0 drivers/usb/core/hub.c:5591
process_one_work+0x98d/0x15c0 kernel/workqueue.c:2275
worker_thread+0x64c/0x1120 kernel/workqueue.c:2421
kthread+0x38c/0x460 kernel/kthread.c:292
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:296

Freed by task 2183:
kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48
kasan_set_track+0x1c/0x30 mm/kasan/common.c:56
kasan_set_free_info+0x1b/0x30 mm/kasan/generic.c:352
__kasan_slab_free+0x102/0x140 mm/kasan/common.c:422
slab_free_hook mm/slub.c:1544 [inline]
slab_free_freelist_hook+0x5d/0x150 mm/slub.c:1577
slab_free mm/slub.c:3140 [inline]
kfree+0xdb/0x3a0 mm/slub.c:4122
device_release+0x9f/0x240 drivers/base/core.c:1962
kobject_cleanup lib/kobject.c:705 [inline]
kobject_release lib/kobject.c:736 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x1c8/0x540 lib/kobject.c:753
put_device+0x1b/0x30 drivers/base/core.c:3190
hub_port_connect drivers/usb/core/hub.c:5074 [inline]
hub_port_connect_change drivers/usb/core/hub.c:5363 [inline]
port_event drivers/usb/core/hub.c:5509 [inline]
hub_event+0x1c8a/0x42d0 drivers/usb/core/hub.c:5591
process_one_work+0x98d/0x15c0 kernel/workqueue.c:2275
worker_thread+0x64c/0x1120 kernel/workqueue.c:2421
kthread+0x38c/0x460 kernel/kthread.c:292
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:296

Last potentially related work creation:
kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48
kasan_record_aux_stack+0xc0/0xf0 mm/kasan/generic.c:343
insert_work+0x48/0x370 kernel/workqueue.c:1331
__queue_work+0x5c3/0xf60 kernel/workqueue.c:1497
queue_work_on+0xc7/0xd0 kernel/workqueue.c:1524
kref_put include/linux/kref.h:65 [inline]
tty_kref_put drivers/tty/tty_io.c:1493 [inline]
release_tty+0x4e9/0x610 drivers/tty/tty_io.c:1530
tty_release_struct+0xb4/0xe0 drivers/tty/tty_io.c:1629
tty_release+0xc70/0x1210 drivers/tty/tty_io.c:1789
__fput+0x288/0x920 fs/file_table.c:280
task_work_run+0xdd/0x1a0 kernel/task_work.c:168
tracehook_notify_resume include/linux/tracehook.h:189 [inline]
exit_to_user_mode_loop kernel/entry/common.c:174 [inline]
exit_to_user_mode_prepare+0x186/0x190 kernel/entry/common.c:201
__syscall_exit_to_user_mode_work kernel/entry/common.c:291 [inline]
syscall_exit_to_user_mode+0x19/0x50 kernel/entry/common.c:302
entry_SYSCALL_64_after_hwframe+0x44/0xa9

The buggy address belongs to the object at ffff888113e9f000
which belongs to the cache kmalloc-2k of size 2048
The buggy address is located 24 bytes inside of
2048-byte region [ffff888113e9f000, ffff888113e9f800)
The buggy address belongs to the page:
page:00000000e5a7bd64 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x113e98
head:00000000e5a7bd64 order:3 compound_mapcount:0 compound_pincount:0
flags: 0x200000000010200(slab|head)
raw: 0200000000010200 dead000000000100 dead000000000122 ffff888100042000
raw: 0000000000000000 0000000000080008 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected

Memory state around the buggy address:
ffff888113e9ef00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888113e9ef80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff888113e9f000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888113e9f080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888113e9f100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================


Tested on:

commit: 5e60366d Merge tag 'fallthrough-fixes-clang-5.11-rc1' of g..
git tree: https://github.com/google/kasan.git
console output: https://syzkaller.appspot.com/x/log.txt?x=1755d077500000
patch: https://syzkaller.appspot.com/x/patch.diff?x=15e05e57500000

Zhang, Qiang

unread,
Jan 4, 2021, 11:50:50 PM1/4/21
to Oliver Neukum, syzbot, andre...@google.com, gre...@linuxfoundation.org, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org, linux-...@vger.kernel.org, linu...@vger.kernel.org, penguin...@i-love.sakura.ne.jp, syzkall...@googlegroups.com

________________________________________
发件人: Oliver Neukum <one...@suse.com>
发送时间: 2021年1月5日 0:28
收件人: syzbot; andre...@google.com; gre...@linuxfoundation.org; gusta...@kernel.org; ingr...@epigenesys.com; lee....@linaro.org; linux-...@vger.kernel.org; linu...@vger.kernel.org; penguin...@I-love.SAKURA.ne.jp; syzkall...@googlegroups.com
主题: Re: KASAN: use-after-free Read in service_outstanding_interrupt

Am Donnerstag, den 17.12.2020, 19:21 -0800 schrieb syzbot:
> syzbot has found a reproducer for the following issue on:
>
> HEAD commit: 5e60366d Merge tag 'fallthrough-fixes-clang-5.11-rc1' of g..
> git tree: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
> console output: https://syzkaller.appspot.com/x/log.txt?x=12c5b623500000
> kernel config: https://syzkaller.appspot.com/x/.config?x=5cea7506b7139727
> dashboard link: https://syzkaller.appspot.com/bug?extid=9e04e2df4a32fb661daf
> compiler: gcc (GCC) 10.1.0-syz 20200507
> syz repro: https://syzkaller.appspot.com/x/repro.syz?x=175adf07500000
> C reproducer: https://syzkaller.appspot.com/x/repro.c?x=1672680f500000
>
> IMPORTANT: if you fix the issue, please add the following tag to the commit:
> Reported-by: >syzbot+9e04e2...@syzkaller.appspotmail.com
>
>#syz test: https://github.com/google/kasan.git 5e60366d
>

Hello Oliver

this use-after-free still exists,It can be seen from calltrace that it is
usb_device's object has been released when disconnect,
can add a reference count to usb_device's object to avoid this problem

diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 508b1c3f8b73..001cb93da6bf 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -106,6 +106,7 @@ struct wdm_device {

struct list_head device_list;
int (*manage_power)(struct usb_interface *, int);
+ struct usb_device *usb_dev;
};

static struct usb_driver wdm_driver;
@@ -338,6 +339,7 @@ static void free_urbs(struct wdm_device *desc)

static void cleanup(struct wdm_device *desc)
{
+ usb_put_dev(desc->usb_dev);
kfree(desc->sbuf);
kfree(desc->inbuf);
kfree(desc->orq);
@@ -855,6 +857,7 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descripto
r
desc->intf = intf;
INIT_WORK(&desc->rxwork, wdm_rxwork);
INIT_WORK(&desc->service_outs_intr, service_interrupt_work);
+ desc->usb_dev = usb_get_dev(interface_to_usbdev(intf));

rv = -EINVAL;
if (!usb_endpoint_is_int_in(ep))

Oliver Neukum

unread,
Jan 5, 2021, 5:51:44 AM1/5/21
to Zhang, Qiang, syzbot, andre...@google.com, gre...@linuxfoundation.org, gusta...@kernel.org, ingr...@epigenesys.com, lee....@linaro.org, linux-...@vger.kernel.org, linu...@vger.kernel.org, penguin...@i-love.sakura.ne.jp, syzkall...@googlegroups.com
Am Dienstag, den 05.01.2021, 04:50 +0000 schrieb Zhang, Qiang:
>
> ________________________________________
> 发件人: Oliver Neukum <one...@suse.com>
> 发送时间: 2021年1月5日 0:28
> 收件人: syzbot; andre...@google.com; gre...@linuxfoundation.org; gusta...@kernel.org; ingr...@epigenesys.com; lee....@linaro.org; linux-...@vger.kernel.org; linu...@vger.kernel.org; penguin...@I-love.SAKURA.ne.jp; syzkall...@googlegroups.com
> 主题: Re: KASAN: use-after-free Read in service_outstanding_interrupt
>
> Am Donnerstag, den 17.12.2020, 19:21 -0800 schrieb syzbot:
> > syzbot has found a reproducer for the following issue on:
> >
> > HEAD commit: 5e60366d Merge tag 'fallthrough-fixes-clang-5.11-rc1' of g..
> > git tree: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
> > console output: https://syzkaller.appspot.com/x/log.txt?x=12c5b623500000
> > kernel config: https://syzkaller.appspot.com/x/.config?x=5cea7506b7139727
> > dashboard link: https://syzkaller.appspot.com/bug?extid=9e04e2df4a32fb661daf
> > compiler: gcc (GCC) 10.1.0-syz 20200507
> > syz repro: https://syzkaller.appspot.com/x/repro.syz?x=175adf07500000
> > C reproducer: https://syzkaller.appspot.com/x/repro.c?x=1672680f500000
> >
> > IMPORTANT: if you fix the issue, please add the following tag to the commit:
> > Reported-by: >syzbot+9e04e2...@syzkaller.appspotmail.com
> >
> > #syz test: https://github.com/google/kasan.git 5e60366d
> >
>
> Hello Oliver
>
> this use-after-free still exists,It can be seen from calltrace that it is
> usb_device's object has been released when disconnect,
> can add a reference count to usb_device's object to avoid this problem

Hi,

thanks for your analysis. I think you are correct in your analysis, but
I am afraid your fix is not correct. The driver is submitting an URB
to a disconnected device. Your fix would prevent a crash, which is
definitely good, but we still cannot do that, because the device may
be owned by another driver or usbfs at that time.

Regards
Oliver


Hillf Danton

unread,
Jan 11, 2021, 3:07:12 AM1/11/21
to syzbot, andre...@google.com, gre...@linuxfoundation.org, Hillf Danton, Qiang...@windriver.com, Oliver Neukum, syzkall...@googlegroups.com
Mon, 04 Jan 2021 08:44:07 -0800
> syzbot has tested the proposed patch but the reproducer is still triggering an issue:
> KASAN: use-after-free Read in service_outstanding_interrupt
>
> ==================================================================
> BUG: KASAN: use-after-free in usb_submit_urb+0x1210/0x1560 drivers/usb/core/urb.c:383
> Read of size 4 at addr ffff888113e9f018 by task syz-executor.0/7799
>
> CPU: 1 PID: 7799 Comm: syz-executor.0 Not tainted 5.10.0-syzkaller #0
> Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
> Call Trace:
> __dump_stack lib/dump_stack.c:79 [inline]
> dump_stack+0x107/0x163 lib/dump_stack.c:120
> print_address_description.constprop.0.cold+0xae/0x4c8 mm/kasan/report.c:385
> __kasan_report mm/kasan/report.c:545 [inline]
> kasan_report.cold+0x1f/0x37 mm/kasan/report.c:562
> usb_submit_urb+0x1210/0x1560 drivers/usb/core/urb.c:383
> service_outstanding_interrupt.part.0+0x5f/0xa0 drivers/usb/class/cdc-wdm.c:470
> service_outstanding_interrupt drivers/usb/class/cdc-wdm.c:465 [inline]
> wdm_read+0x9a0/0xbd0 drivers/usb/class/cdc-wdm.c:583

The file's open count failed to prevent private_data from being released.
Because opener and disconnector are serialized with the wdm_mutex
lock, replace file open count with struct completion, and disconnector
will not progress without waiting for every opener to go home.

Note the wdm_mutex memory slice will no longer be freed on the file
release path because file opener is just finding a valid device to
use rather than creating one.

--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -89,7 +89,7 @@ struct ywwdm_device {
int reslength;
int length;
int read;
- int count;
+ struct completion opener;
dma_addr_t shandle;
dma_addr_t ihandle;
struct mutex wlock;
@@ -636,18 +636,21 @@ desc_out:
static int wdm_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
- int rv = -ENODEV;
- struct usb_interface *intf;
struct wdm_device *desc;
+ int rv = -ENODEV;

mutex_lock(&wdm_mutex);
desc = wdm_find_device_by_minor(minor);
if (!desc)
goto out;

- intf = desc->intf;
if (test_bit(WDM_DISCONNECTING, &desc->flags))
goto out;
+
+ if (file->private_data == desc) {
+ rv = 0;
+ goto out;
+ }
file->private_data = desc;

rv = usb_autopm_get_interface(desc->intf);
@@ -656,24 +659,20 @@ static int wdm_open(struct inode *inode,
goto out;
}

- /* using write lock to protect desc->count */
mutex_lock(&desc->wlock);
- if (!desc->count++) {
- desc->werr = 0;
- desc->rerr = 0;
- rv = usb_submit_urb(desc->validity, GFP_KERNEL);
- if (rv < 0) {
- desc->count--;
- dev_err(&desc->intf->dev,
- "Error submitting int urb - %d\n", rv);
- rv = usb_translate_errors(rv);
- }
+ desc->werr = 0;
+ desc->rerr = 0;
+ rv = usb_submit_urb(desc->validity, GFP_KERNEL);
+ if (rv < 0) {
+ dev_err(&desc->intf->dev,
+ "Error submitting int urb - %d\n", rv);
+ rv = usb_translate_errors(rv);
} else {
- rv = 0;
+ reinit_completion(&desc->opener);
}
mutex_unlock(&desc->wlock);
- if (desc->count == 1)
- desc->manage_power(intf, 1);
+
+ desc->manage_power(desc->intf, 1);
usb_autopm_put_interface(desc->intf);
out:
mutex_unlock(&wdm_mutex);
@@ -686,25 +685,9 @@ static int wdm_release(struct inode *ino

mutex_lock(&wdm_mutex);

- /* using write lock to protect desc->count */
- mutex_lock(&desc->wlock);
- desc->count--;
- mutex_unlock(&desc->wlock);
-
- if (!desc->count) {
- if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
- dev_dbg(&desc->intf->dev, "wdm_release: cleanup\n");
- kill_urbs(desc);
- spin_lock_irq(&desc->iuspin);
- desc->resp_count = 0;
- spin_unlock_irq(&desc->iuspin);
- desc->manage_power(desc->intf, 0);
- } else {
- /* must avoid dev_printk here as desc->intf is invalid */
- pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__);
- cleanup(desc);
- }
- }
+ file->private_data = NULL;
+ complete(&desc->opener);
+
mutex_unlock(&wdm_mutex);
return 0;
}
@@ -797,6 +780,8 @@ static int wdm_create(struct usb_interfa
if (!desc)
goto out;
INIT_LIST_HEAD(&desc->device_list);
+ init_completion(&desc->opener);
+ complete(&desc->opener);
mutex_init(&desc->rlock);
mutex_init(&desc->wlock);
spin_lock_init(&desc->iuspin);
@@ -1000,11 +985,10 @@ static void wdm_disconnect(struct usb_in
list_del(&desc->device_list);
spin_unlock(&wdm_device_list_lock);

- if (!desc->count)
- cleanup(desc);
- else
- dev_dbg(&intf->dev, "%d open files - postponing cleanup\n", desc->count);
mutex_unlock(&wdm_mutex);
+
+ wait_for_completion(&desc->opener);
+ cleanup(desc);
}

#ifdef CONFIG_PM
@@ -1045,11 +1029,19 @@ static int wdm_suspend(struct usb_interf
}
#endif

+static bool __completion_done(struct completion *c)
+{
+ if (READ_ONCE(c->done))
+ return true;
+ else
+ return false;
+}
+
static int recover_from_urb_loss(struct wdm_device *desc)
{
int rv = 0;

- if (desc->count) {
+ if (!__completion_done(&desc->opener)) {
rv = usb_submit_urb(desc->validity, GFP_NOIO);
if (rv < 0)
dev_err(&desc->intf->dev,
Reply all
Reply to author
Forward
0 new messages