diff mbox series

UAF write in usb_audio_probe

Message ID 2a8165ce-7c75-0e73-64e3-269826adf964@nebelwelt.net (mailing list archive)
State New, archived
Headers show
Series UAF write in usb_audio_probe | expand

Commit Message

Mathias Payer Dec. 3, 2018, 1:40 p.m. UTC
Hi there,

We are submitting a patch for an UAF write in usb_audio_probe. A malicious USB
device can send a broken device configuration that will trigger a free of the
underlying object before it is decremented. I'm sending the patch and KASAN
report after discussing with Takashi on the security mailing list so that he can
merge.

The attacker needs local access to plug in a malicious USB device that replays
the trace (e.g., through FaceDancer) to get read/write primitives in the kernel.
For, e.g., Android or locked Desktops this becomes security critical.

This bug is present in 4.14.81 through to HEAD (i.e., all versions we tested).

The place where UAF is triggered is in `usb_audio_probe` in `sound/usb/card.c`.
In the error handling code in `usb_audio_probe`,  if the number of interfaces
read from the device side is zero, `chip->card` object is freed. Unfortunately,
the `chip` object is part of the `card` object, thus, when `chip->card` is
freed, `chip` is also freed.

However, in the error handling code, the `active` field will be written to
by an `atomic_dec` operation, resulting in an UAF write.

```
static int usb_audio_probe(struct usb_interface *intf,
							const struct usb_device_id *usb_id)
.....

 __error:
if (chip) {
  		if (!chip->num_interfaces)
  			snd_card_free(chip->card);
  		// UAF write
  		atomic_dec(&chip->active);
  	}
  	mutex_unlock(&register_mutex);
  	return err;

```

The attacker can race the snd_card_free and the atomic_dec by attaching new USB
devices (the attacker can time when what parts of the first malicious device are
read then play with attaching the second device; the race is arbitrarily
repeatable). The new USB device descriptors will use the recently freed memory
which is then modified by the atomic_dec, resulting in an attacker-controlled
decrement operation to, e.g., a USB data structure under the attacker's control.

We have also attached the KASAN report from when the bug was triggered.

The patch for card.c is attached. It moves the atomic_dec to above the free of
the chip memory object, ensuring that, if no cards are left, the decrement
happens before the free. We've also added a comment to clarify that the two
objects are dependent.

Thanks,
Hui Peng
Mathias Payer
BUG: KASAN: use-after-free in atomic_dec include/asm-generic/atomic-instrumented.h:114 [inline]
BUG: KASAN: use-after-free in usb_audio_probe+0xee7/0x2220 sound/usb/card.c:687
Write of size 4 at addr ffff88012f91d114 by task test/5641

CPU: 2 PID: 5641 Comm: test Not tainted 4.18.19 #1
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
Call Trace:
 __dump_stack lib/dump_stack.c:77 [inline]
 dump_stack+0xf2/0x188 lib/dump_stack.c:113
 print_address_description+0x71/0x239 mm/kasan/report.c:256
 kasan_report_error mm/kasan/report.c:354 [inline]
 kasan_report.cold.6+0x242/0x2fe mm/kasan/report.c:412
 check_memory_region_inline mm/kasan/kasan.c:260 [inline]
 check_memory_region+0x13e/0x1b0 mm/kasan/kasan.c:267
 kasan_check_write+0x14/0x20 mm/kasan/kasan.c:278
 atomic_dec include/asm-generic/atomic-instrumented.h:114 [inline]
 usb_audio_probe+0xee7/0x2220 sound/usb/card.c:687
 usb_probe_interface+0x324/0x940 drivers/usb/core/driver.c:361
 really_probe drivers/base/dd.c:438 [inline]
 driver_probe_device+0x598/0x7f0 drivers/base/dd.c:580
 __device_attach_driver+0x25f/0x2e0 drivers/base/dd.c:676
 bus_for_each_drv+0x124/0x190 drivers/base/bus.c:461
 __device_attach+0x1ca/0x2d0 drivers/base/dd.c:733
 device_initial_probe+0x1f/0x30 drivers/base/dd.c:780
 bus_probe_device+0x200/0x2b0 drivers/base/bus.c:521
 device_add+0x8b5/0x1460 drivers/base/core.c:1875
 usb_set_configuration+0xd5f/0x1780 drivers/usb/core/message.c:2014
 generic_probe+0xbb/0x120 drivers/usb/core/generic.c:174
 usb_probe_device+0xb4/0x110 drivers/usb/core/driver.c:266
 really_probe drivers/base/dd.c:438 [inline]
 driver_probe_device+0x598/0x7f0 drivers/base/dd.c:580
 __device_attach_driver+0x25f/0x2e0 drivers/base/dd.c:676
 bus_for_each_drv+0x124/0x190 drivers/base/bus.c:461
 __device_attach+0x1ca/0x2d0 drivers/base/dd.c:733
 device_initial_probe+0x1f/0x30 drivers/base/dd.c:780
 bus_probe_device+0x200/0x2b0 drivers/base/bus.c:521
 device_add+0x8b5/0x1460 drivers/base/core.c:1875
 usb_new_device+0x9f9/0x1f30 drivers/usb/core/hub.c:2563
 hub_port_connect drivers/usb/core/hub.c:5067 [inline]
 hub_port_connect_change drivers/usb/core/hub.c:5182 [inline]
 port_event drivers/usb/core/hub.c:5290 [inline]
 hub_event_impl+0x189b/0x3670 drivers/usb/core/hub.c:5402
 uf_hub_events_handle+0x504/0x53e drivers/usb/core/hub.c:1874
 hub_ioctl+0x4fb/0x550 drivers/usb/core/hub.c:1924
 proc_ioctl+0x3e1/0x610 drivers/usb/core/devio.c:2187
 proc_ioctl_default drivers/usb/core/devio.c:2210 [inline]
 usbdev_do_ioctl+0x61a/0x2e20 drivers/usb/core/devio.c:2524
 usbdev_ioctl+0x2a/0x40 drivers/usb/core/devio.c:2568
 vfs_ioctl fs/ioctl.c:46 [inline]
 file_ioctl fs/ioctl.c:500 [inline]
 do_vfs_ioctl+0x16d/0x1160 fs/ioctl.c:684
 ksys_ioctl+0xae/0xd0 fs/ioctl.c:701
 __do_sys_ioctl fs/ioctl.c:708 [inline]
 __se_sys_ioctl fs/ioctl.c:706 [inline]
 __x64_sys_ioctl+0x78/0xb0 fs/ioctl.c:706
 do_syscall_64+0xd4/0x4e0 arch/x86/entry/common.c:290
 entry_SYSCALL_64_after_hwframe+0x49/0xbe
RIP: 0033:0x452d07
Code: 48 83 c4 08 48 89 d8 5b 5d c3 66 0f 1f 84 00 00 00 00 00 48 89 e8 48 f7 d8 48 39 c3 0f 92 c0 eb 92 66 90 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 0f 83 7d bb fb ff c3 66 66 66 2e 0f 1f 84 00 00 
RSP: 002b:00007fff9dea3de8 EFLAGS: 00000206 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 0000000000452d07
RDX: 00007fff9dea3e00 RSI: 00000000c0105512 RDI: 0000000000000000
RBP: 0000000020004ff2 R08: 0000000020000a74 R09: 0000000020001000
R10: 0000000000000004 R11: 0000000000000206 R12: 0000000000000074
R13: 0000000020000a74 R14: 0000000000000004 R15: 0000000000000003

Allocated by task 5641:
 save_stack+0x43/0xd0 mm/kasan/kasan.c:448
 set_track mm/kasan/kasan.c:460 [inline]
 kasan_kmalloc+0xc7/0xe0 mm/kasan/kasan.c:553
 __kmalloc+0x161/0x330 mm/slub.c:3753
 kmalloc include/linux/slab.h:518 [inline]
 kzalloc include/linux/slab.h:707 [inline]
 snd_card_new+0x92/0x8b0 sound/core/init.c:214
 snd_usb_audio_create sound/usb/card.c:475 [inline]
 usb_audio_probe+0x12da/0x2220 sound/usb/card.c:621
 usb_probe_interface+0x324/0x940 drivers/usb/core/driver.c:361
 really_probe drivers/base/dd.c:438 [inline]
 driver_probe_device+0x598/0x7f0 drivers/base/dd.c:580
 __device_attach_driver+0x25f/0x2e0 drivers/base/dd.c:676
 bus_for_each_drv+0x124/0x190 drivers/base/bus.c:461
 __device_attach+0x1ca/0x2d0 drivers/base/dd.c:733
 device_initial_probe+0x1f/0x30 drivers/base/dd.c:780
 bus_probe_device+0x200/0x2b0 drivers/base/bus.c:521
 device_add+0x8b5/0x1460 drivers/base/core.c:1875
 usb_set_configuration+0xd5f/0x1780 drivers/usb/core/message.c:2014
 generic_probe+0xbb/0x120 drivers/usb/core/generic.c:174
 usb_probe_device+0xb4/0x110 drivers/usb/core/driver.c:266
 really_probe drivers/base/dd.c:438 [inline]
 driver_probe_device+0x598/0x7f0 drivers/base/dd.c:580
 __device_attach_driver+0x25f/0x2e0 drivers/base/dd.c:676
 bus_for_each_drv+0x124/0x190 drivers/base/bus.c:461
 __device_attach+0x1ca/0x2d0 drivers/base/dd.c:733
 device_initial_probe+0x1f/0x30 drivers/base/dd.c:780
 bus_probe_device+0x200/0x2b0 drivers/base/bus.c:521
 device_add+0x8b5/0x1460 drivers/base/core.c:1875
 usb_new_device+0x9f9/0x1f30 drivers/usb/core/hub.c:2563
 hub_port_connect drivers/usb/core/hub.c:5067 [inline]
 hub_port_connect_change drivers/usb/core/hub.c:5182 [inline]
 port_event drivers/usb/core/hub.c:5290 [inline]
 hub_event_impl+0x189b/0x3670 drivers/usb/core/hub.c:5402
 uf_hub_events_handle+0x504/0x53e drivers/usb/core/hub.c:1874
 hub_ioctl+0x4fb/0x550 drivers/usb/core/hub.c:1924
 proc_ioctl+0x3e1/0x610 drivers/usb/core/devio.c:2187
 proc_ioctl_default drivers/usb/core/devio.c:2210 [inline]
 usbdev_do_ioctl+0x61a/0x2e20 drivers/usb/core/devio.c:2524
 usbdev_ioctl+0x2a/0x40 drivers/usb/core/devio.c:2568
 vfs_ioctl fs/ioctl.c:46 [inline]
 file_ioctl fs/ioctl.c:500 [inline]
 do_vfs_ioctl+0x16d/0x1160 fs/ioctl.c:684
 ksys_ioctl+0xae/0xd0 fs/ioctl.c:701
 __do_sys_ioctl fs/ioctl.c:708 [inline]
 __se_sys_ioctl fs/ioctl.c:706 [inline]
 __x64_sys_ioctl+0x78/0xb0 fs/ioctl.c:706
 do_syscall_64+0xd4/0x4e0 arch/x86/entry/common.c:290
 entry_SYSCALL_64_after_hwframe+0x49/0xbe

Freed by task 5641:
 save_stack+0x43/0xd0 mm/kasan/kasan.c:448
 set_track mm/kasan/kasan.c:460 [inline]
 __kasan_slab_free+0x139/0x190 mm/kasan/kasan.c:521
 kasan_slab_free+0xe/0x10 mm/kasan/kasan.c:528
 slab_free_hook mm/slub.c:1373 [inline]
 slab_free_freelist_hook mm/slub.c:1400 [inline]
 slab_free mm/slub.c:2955 [inline]
 kfree+0x107/0x310 mm/slub.c:3908
 snd_card_do_free sound/core/init.c:501 [inline]
 release_card_device+0x116/0x180 sound/core/init.c:181
 device_release+0x83/0x210 drivers/base/core.c:850
 kobject_cleanup lib/kobject.c:643 [inline]
 kobject_release lib/kobject.c:672 [inline]
 kref_put include/linux/kref.h:70 [inline]
 kobject_put+0x198/0x290 lib/kobject.c:689
 put_device+0x25/0x30 drivers/base/core.c:1972
 snd_card_free_when_closed sound/core/init.c:519 [inline]
 snd_card_free+0x9a/0xd0 sound/core/init.c:544
 usb_audio_probe+0x108f/0x2220 sound/usb/card.c:686
 usb_probe_interface+0x324/0x940 drivers/usb/core/driver.c:361
 really_probe drivers/base/dd.c:438 [inline]
 driver_probe_device+0x598/0x7f0 drivers/base/dd.c:580
 __device_attach_driver+0x25f/0x2e0 drivers/base/dd.c:676
 bus_for_each_drv+0x124/0x190 drivers/base/bus.c:461
 __device_attach+0x1ca/0x2d0 drivers/base/dd.c:733
 device_initial_probe+0x1f/0x30 drivers/base/dd.c:780
 bus_probe_device+0x200/0x2b0 drivers/base/bus.c:521
 device_add+0x8b5/0x1460 drivers/base/core.c:1875
 usb_set_configuration+0xd5f/0x1780 drivers/usb/core/message.c:2014
 generic_probe+0xbb/0x120 drivers/usb/core/generic.c:174
 usb_probe_device+0xb4/0x110 drivers/usb/core/driver.c:266
 really_probe drivers/base/dd.c:438 [inline]
 driver_probe_device+0x598/0x7f0 drivers/base/dd.c:580
 __device_attach_driver+0x25f/0x2e0 drivers/base/dd.c:676
 bus_for_each_drv+0x124/0x190 drivers/base/bus.c:461
 __device_attach+0x1ca/0x2d0 drivers/base/dd.c:733
 device_initial_probe+0x1f/0x30 drivers/base/dd.c:780
 bus_probe_device+0x200/0x2b0 drivers/base/bus.c:521
 device_add+0x8b5/0x1460 drivers/base/core.c:1875
 usb_new_device+0x9f9/0x1f30 drivers/usb/core/hub.c:2563
 hub_port_connect drivers/usb/core/hub.c:5067 [inline]
 hub_port_connect_change drivers/usb/core/hub.c:5182 [inline]
 port_event drivers/usb/core/hub.c:5290 [inline]
 hub_event_impl+0x189b/0x3670 drivers/usb/core/hub.c:5402
 uf_hub_events_handle+0x504/0x53e drivers/usb/core/hub.c:1874
 hub_ioctl+0x4fb/0x550 drivers/usb/core/hub.c:1924
 proc_ioctl+0x3e1/0x610 drivers/usb/core/devio.c:2187
 proc_ioctl_default drivers/usb/core/devio.c:2210 [inline]
 usbdev_do_ioctl+0x61a/0x2e20 drivers/usb/core/devio.c:2524
 usbdev_ioctl+0x2a/0x40 drivers/usb/core/devio.c:2568
 vfs_ioctl fs/ioctl.c:46 [inline]
 file_ioctl fs/ioctl.c:500 [inline]
 do_vfs_ioctl+0x16d/0x1160 fs/ioctl.c:684
 ksys_ioctl+0xae/0xd0 fs/ioctl.c:701
 __do_sys_ioctl fs/ioctl.c:708 [inline]
 __se_sys_ioctl fs/ioctl.c:706 [inline]
 __x64_sys_ioctl+0x78/0xb0 fs/ioctl.c:706
 do_syscall_64+0xd4/0x4e0 arch/x86/entry/common.c:290
 entry_SYSCALL_64_after_hwframe+0x49/0xbe

The buggy address belongs to the object at ffff88012f91c400
 which belongs to the cache kmalloc-4096 of size 4096
The buggy address is located 3348 bytes inside of
 4096-byte region [ffff88012f91c400, ffff88012f91d400)
The buggy address belongs to the page:
page:ffffea0004be4600 count:1 mapcount:0 mapping:ffff880064802c00 index:0x0 compound_mapcount: 0
flags: 0x600000000008100(slab|head)
raw: 0600000000008100 0000000000000000 0000000100000001 ffff880064802c00
raw: 0000000000000000 0000000080070007 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected

Memory state around the buggy address:
 ffff88012f91d000: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
 ffff88012f91d080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff88012f91d100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
                         ^
 ffff88012f91d180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
 ffff88012f91d200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================

Comments

Takashi Iwai Dec. 3, 2018, 3:23 p.m. UTC | #1
On Mon, 03 Dec 2018 14:40:19 +0100,
Mathias Payer wrote:
> 
> Hi there,
> 
> We are submitting a patch for an UAF write in usb_audio_probe. A malicious USB
> device can send a broken device configuration that will trigger a free of the
> underlying object before it is decremented. I'm sending the patch and KASAN
> report after discussing with Takashi on the security mailing list so that he can
> merge.
> 
> The attacker needs local access to plug in a malicious USB device that replays
> the trace (e.g., through FaceDancer) to get read/write primitives in the kernel.
> For, e.g., Android or locked Desktops this becomes security critical.
> 
> This bug is present in 4.14.81 through to HEAD (i.e., all versions we tested).
> 
> The place where UAF is triggered is in `usb_audio_probe` in `sound/usb/card.c`.
> In the error handling code in `usb_audio_probe`,  if the number of interfaces
> read from the device side is zero, `chip->card` object is freed. Unfortunately,
> the `chip` object is part of the `card` object, thus, when `chip->card` is
> freed, `chip` is also freed.
> 
> However, in the error handling code, the `active` field will be written to
> by an `atomic_dec` operation, resulting in an UAF write.
> 
> ```
> static int usb_audio_probe(struct usb_interface *intf,
> 							const struct usb_device_id *usb_id)
> .....
> 
>  __error:
> if (chip) {
>   		if (!chip->num_interfaces)
>   			snd_card_free(chip->card);
>   		// UAF write
>   		atomic_dec(&chip->active);
>   	}
>   	mutex_unlock(&register_mutex);
>   	return err;
> 
> ```
> 
> The attacker can race the snd_card_free and the atomic_dec by attaching new USB
> devices (the attacker can time when what parts of the first malicious device are
> read then play with attaching the second device; the race is arbitrarily
> repeatable). The new USB device descriptors will use the recently freed memory
> which is then modified by the atomic_dec, resulting in an attacker-controlled
> decrement operation to, e.g., a USB data structure under the attacker's control.
> 
> We have also attached the KASAN report from when the bug was triggered.
> 
> The patch for card.c is attached. It moves the atomic_dec to above the free of
> the chip memory object, ensuring that, if no cards are left, the decrement
> happens before the free. We've also added a comment to clarify that the two
> objects are dependent.

Thanks for the report.  I applied the patch now in for-linus branch.


Takashi
diff mbox series

Patch

commit 37685fb0b2d13d676038f8602569d1e5faa29ef5
Author: Mathias Payer <mathias.payer@nebelwelt.net>
Date:   Mon Dec 3 05:09:54 2018 +0100

    ALSA: usb-audio: Fix UAF decrement if card has no live interfaces in card.c
    
    From: Hui Peng <benquike@gmail.com>
    
    If a USB sound card reports 0 interfaces, an error condition is triggered
    and the function usb_audio_probe errors out. In the error path, there was a
    use-after-free vulnerability where the memory object of the card was first
    freed, followed by a decrement of the number of active chips. Moving the
    decrement above the atomic_dec fixes the UAF.
    
    Reported-by: Hui Peng <benquike@gmail.com>
    Reported-by: Mathias Payer <mathias.payer@nebelwelt.net>
    Signed-off-by: Hui Peng <benquike@gmail.com>
    Signed-off-by: Mathias Payer <mathias.payer@nebelwelt.net>

diff --git a/sound/usb/card.c b/sound/usb/card.c
index 2bfe4e80a6b9..3ea9c125731c 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -682,9 +682,12 @@  static int usb_audio_probe(struct usb_interface *intf,
 
  __error:
 	if (chip) {
+		/* chip->active is inside the chip->card object,
+		 * decrement before memory is possibly returned.
+		 */
+		atomic_dec(&chip->active);
 		if (!chip->num_interfaces)
 			snd_card_free(chip->card);
-		atomic_dec(&chip->active);
 	}
 	mutex_unlock(&register_mutex);
 	return err;