summaryrefslogtreecommitdiff
path: root/include/uapi
diff options
context:
space:
mode:
authorCen Zhang <zzzccc427@gmail.com>2026-10-14 12:06:52 +0800
committerTakashi Iwai <tiwai@suse.de>2026-10-14 08:55:18 +0200
commitab669ac2bc418a3ef1a1541ea6e1ef5dc8c87a3f (patch)
tree1429ac5572b3dd7f6b73ca00bbf4315f60a42f58 /include/uapi
parentd6e4158d92936987d963040c8ae2657a328459b8 (diff)
downloadlinux-next-ab669ac2bc418a3ef1a1541ea6e1ef5dc8c87a3f.tar.gz
linux-next-ab669ac2bc418a3ef1a1541ea6e1ef5dc8c87a3f.zip
ALSA: seq: oss: Prevent duplicate MIDI device teardown
Each OSS MIDI alias and its coder must have one final-release owner. find_slot() pins the alias under register_lock, but snd_seq_oss_midi_check_exit_port() clears its slot in a separate critical section without checking whether another callback already removed it. The use counter protects active users, but draining it does not elect a single remover. This requires overlapping raw kernel-client port controls for the same client. snd_seq_kernel_client_ctl() does not take ioctl_mutex, and snd_seq_delete_port() releases ports_mutex before returning to the DELETE_PORT handler's PORT_EXIT broadcast. If the old deletion is delayed before that broadcast, a new port can reuse the vacant address. Its PORT_START finds and keeps the old alias. Deleting the replacement then produces a second PORT_EXIT for the same address. Non-atomic subscriber delivery holds a read rwsem, so the two receive_announce() callbacks can run concurrently: Old-port deletion Replacement-port controls ----------------- ------------------------- Unlink and drain old port Delay before PORT_EXIT Create port at the same address PORT_START keeps existing alias Delete port; PORT_EXIT pins mdev PORT_EXIT pins the same mdev Clear slot; drop pin Clear slot; drop pin Wait for users to drain Wait for users to drain Free coder; pause before kfree(mdev) Free coder again: read freed coder->buf Both callbacks can select the same alias before either clears its slot. After both drop their pins, both waits can return. The second call to snd_midi_event_free() can then read the freed coder's buffer pointer, as in the report below, and attempt to release the same objects twice. Check that the pinned alias still occupies its slot under register_lock before clearing it. A callback that finds it already removed drops its pin and returns. Only the callback that removes the alias drains users and frees the coder and record. The pin keeps the record alive through this check, and the wait remains outside the spinlock. KASAN report as below: ================================================================== BUG: KASAN: slab-use-after-free in snd_midi_event_free+0x53/0x60 Read of size 8 at addr ffff888107074518 by task alsa-seq-fixtur/492 CPU: 3 UID: 0 PID: 492 Comm: alsa-seq-fixtur Not tainted 7.2.0-rc6-pmb-bt-functional-v1+ #1 PREEMPT(lazy) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: <TASK> dump_stack_lvl+0x93/0xd0 print_report+0xce/0x630 ? snd_midi_event_free+0x53/0x60 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20d/0x410 ? snd_midi_event_free+0x53/0x60 kasan_report+0xe0/0x110 ? snd_midi_event_free+0x53/0x60 snd_midi_event_free+0x53/0x60 snd_seq_oss_midi_check_exit_port+0x1d8/0x370 receive_announce+0x1b8/0x370 ? __pfx_receive_announce+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __lock_acquire+0x466/0x2260 ? snd_seq_port_use_ptr+0x40/0x170 ? srso_alias_return_thunk+0x5/0xfbef5 ? find_held_lock+0x2b/0x80 ? snd_seq_port_use_ptr+0x10c/0x170 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x280 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_receive_announce+0x10/0x10 __snd_seq_deliver_single_event+0x105/0x1a0 ? snd_seq_port_use_ptr+0x111/0x170 snd_seq_deliver_single_event+0x2fc/0x770 ? __pfx_snd_seq_deliver_single_event+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_down_read_nested+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? do_raw_read_unlock+0x49/0xe0 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_read_unlock+0x23/0x40 snd_seq_deliver_event+0x351/0x760 ? __pfx_snd_seq_deliver_event+0x10/0x10 ? _raw_spin_unlock_irqrestore+0x44/0x60 ? trace_hardirqs_on+0x18/0x160 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x44/0x60 ? srso_alias_return_thunk+0x5/0xfbef5 snd_seq_kernel_client_dispatch+0x13f/0x170 snd_seq_system_broadcast+0x100/0x150 ? __pfx_snd_seq_system_broadcast+0x10/0x10 ? mark_held_locks+0x40/0x70 ? _raw_spin_unlock_irqrestore+0x44/0x60 ? srso_alias_return_thunk+0x5/0xfbef5 ? lockdep_hardirqs_on_prepare+0xea/0x1a0 ? srso_alias_return_thunk+0x5/0xfbef5 snd_seq_ioctl_delete_port+0x179/0x4b0 call_seq_client_ctl+0x90/0x110 snd_seq_kernel_client_ctl+0x8a/0x100 pmbd_case_delete_port+0xa6/0xf0 ? __pfx_pmbd_case_delete_port+0x10/0x10 ? __might_fault+0xdd/0x190 ? srso_alias_return_thunk+0x5/0xfbef5 ? __might_fault+0x14e/0x190 ? srso_alias_return_thunk+0x5/0xfbef5 ? strim+0x19c/0x200 pmbd_case_control_write+0x2c5/0x470 ? __pfx_pmbd_case_control_write+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __debugfs_file_get+0x1bd/0x7f0 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x17b/0x2f0 ? ksys_write+0x111/0x200 full_proxy_write+0x11f/0x180 vfs_write+0x25a/0x1010 ? __pfx_full_proxy_write+0x10/0x10 ? __pfx___mutex_lock+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x17b/0x2f0 ? __pfx_vfs_write+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? __fget_files+0x1e1/0x370 ksys_write+0x111/0x200 ? __pfx_ksys_write+0x10/0x10 ? rseq_register.isra.0+0x1f3/0x2e0 ? srso_alias_return_thunk+0x5/0xfbef5 ? exit_to_user_mode_loop+0x3a5/0x570 ? srso_alias_return_thunk+0x5/0xfbef5 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f3532e4e9ee Code: 08 0f 85 f5 4b ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 80 00 00 00 00 48 83 ec 08 RSP: 002b:00007f3532dafe08 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 00007f3532db06c0 RCX: 00007f3532e4e9ee RDX: 000000000000000a RSI: 000055efbd747156 RDI: 0000000000000003 RBP: 000055efbd747156 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 000000000000000a R13: 0000000000000000 R14: 00007ffe7894e7f0 R15: 00007f35325b0000 </TASK> Allocated by task 490: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kmalloc_cache_noprof+0x251/0x610 snd_midi_event_new+0x74/0x210 snd_seq_oss_midi_check_new_port+0x329/0x610 receive_announce+0x277/0x370 __snd_seq_deliver_single_event+0x105/0x1a0 snd_seq_deliver_single_event+0x2fc/0x770 snd_seq_deliver_event+0x351/0x760 snd_seq_kernel_client_dispatch+0x13f/0x170 snd_seq_system_broadcast+0x100/0x150 snd_seq_ioctl_create_port+0x39a/0x9d0 call_seq_client_ctl+0x90/0x110 snd_seq_kernel_client_ctl+0x8a/0x100 pmbd_case_create_port+0xcc/0x160 pmbd_case_control_write+0x21d/0x470 full_proxy_write+0x11f/0x180 vfs_write+0x25a/0x1010 ksys_write+0x111/0x200 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 493: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x307/0x580 snd_midi_event_free+0x48/0x60 snd_seq_oss_midi_check_exit_port+0x1d8/0x370 receive_announce+0x1b8/0x370 __snd_seq_deliver_single_event+0x105/0x1a0 snd_seq_deliver_single_event+0x2fc/0x770 snd_seq_deliver_event+0x351/0x760 snd_seq_kernel_client_dispatch+0x13f/0x170 snd_seq_system_broadcast+0x100/0x150 snd_seq_ioctl_delete_port+0x179/0x4b0 call_seq_client_ctl+0x90/0x110 snd_seq_kernel_client_ctl+0x8a/0x100 pmbd_case_delete_port+0xa6/0xf0 pmbd_case_control_write+0x398/0x470 full_proxy_write+0x11f/0x180 vfs_write+0x25a/0x1010 ksys_write+0x111/0x200 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ffff888107074500 which belongs to the cache kmalloc-96 of size 96 The buggy address is located 24 bytes inside of freed 96-byte region [ffff888107074500, ffff888107074560) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x107074 flags: 0x200000000000000(node=0|zone=2) page_type: f5(slab) raw: 0200000000000000 ffff888100042280 dead000000000122 0000000000000000 raw: 0000000000000000 0000000000200020 00000000f5000000 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff888107074400: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff888107074480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff888107074500: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc ^ ffff888107074580: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc ffff888107074600: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc ================================================================== Assisted-by: LLM Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Link: https://patch.msgid.link/pm-pmb-7c5128efe2110a87b872-v2-c718183fd693e73d8e95@gmail.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'include/uapi')
0 files changed, 0 insertions, 0 deletions