mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] ALSA: jack: Remove debugfs before freeing unregistered jacks
@ 2026-10-11 15:55 Cen Zhang
  2026-10-12  7:28 ` Takashi Iwai
  0 siblings, 1 reply; 2+ messages in thread
From: Cen Zhang @ 2026-10-11 15:55 UTC (permalink / raw)
  To: perex, tiwai, phucduc.bui, kees, blum
  Cc: linux-sound, linux-kernel, baijiaju1990, jjzuming, zzzccc427

Jack debugfs files store a raw snd_jack_kctl pointer, so their
callbacks must be drained before the attachment is freed.
snd_jack_dev_free() unlinks and frees the attachments before calling
snd_jack_dev_disconnect(). Its late snd_jack_remove_debugfs() call
then has no entries left to traverse.

With CONFIG_SND_JACK_INJECTION_DEBUG and input support enabled, an
HDA codec rebind on a live card can publish a non-phantom jack's type
file while its component is still in SNDRV_DEV_BUILD. A later
jack-control allocation failure permits the following order:

  1. hda_codec_driver_probe() creates a jack and publishes its debugfs
     files before reaching snd_card_register().
  2. A later control-build failure calls
     snd_hda_codec_cleanup_for_unbind(), then snd_hda_jack_tbl_clear()
     and snd_device_free(). __snd_device_disconnect() skips BUILD
     components. snd_jack_dev_free() frees the attachment before its
     late disconnect, leaving the debugfs files published.
  3. After cleanup, a reader opens the surviving type file and
     jack_type_read() dereferences the freed attachment's jack pointer.

The card's debugfs root remains live throughout this sequence. The
result is a use-after-free even without an overlapping read. An
already admitted callback is also not drained by this cleanup.

Call snd_jack_remove_debugfs() before unlinking any controls in the
free callback. The existing debugfs_remove() rundown blocks new
accesses and waits for active callbacks while the attachment, control
and jack remain valid. Roots cleared by an earlier disconnect are
NULL, so repeating the removal is harmless.

KASAN report as below:

    ==================================================================
    BUG: KASAN: slab-use-after-free in jack_type_read+0x1da/0x1f0
    Read of size 8 at addr ffff8881085f9da0 by task python3/429

    CPU: 2 UID: 0 PID: 429 Comm: python3 Not tainted 7.2.0-rc6-pmb-alsa-functional-v2+ #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
     ? jack_type_read+0x1da/0x1f0
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? __virt_addr_valid+0x20d/0x410
     ? jack_type_read+0x1da/0x1f0
     kasan_report+0xe0/0x110
     ? jack_type_read+0x1da/0x1f0
     jack_type_read+0x1da/0x1f0
     ? __pfx_jack_type_read+0x10/0x10
     ? __pfx___debugfs_file_get+0x10/0x10
     ? __pfx_cp_new_stat+0x10/0x10
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? selinux_file_permission+0x122/0x570
     full_proxy_read+0x11f/0x180
     ? __pfx_full_proxy_read+0x10/0x10
     vfs_read+0x1a0/0xc20
     ? __pfx_vfs_read+0x10/0x10
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? __do_sys_newfstat+0xa6/0x100
     ? find_held_lock+0x2b/0x80
     ? __pfx___do_sys_newfstat+0x10/0x10
     ksys_read+0x111/0x200
     ? __pfx_ksys_read+0x10/0x10
     ? full_proxy_llseek+0x11e/0x170
     do_syscall_64+0x115/0x6a0
     entry_SYSCALL_64_after_hwframe+0x77/0x7f
    RIP: 0033:0x7f7e392e7687
    Code: 48 89 fa 4c 89 df e8 58 b3 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
    RSP: 002b:00007fff5f7b9180 EFLAGS: 00000202 ORIG_RAX: 0000000000000000
    RAX: ffffffffffffffda RBX: 00007f7e39253780 RCX: 00007f7e392e7687
    RDX: 0000000000002000 RSI: 000000003d2e9b00 RDI: 0000000000000003
    RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
    R10: 0000000000000000 R11: 0000000000000202 R12: 00007f7e39253700
    R13: 0000000000002000 R14: 00007f7e38d7e2c0 R15: 0000000000a55b98
     </TASK>

    Allocated by task 425:
     kasan_save_stack+0x33/0x60
     kasan_save_track+0x14/0x30
     __kasan_kmalloc+0xaa/0xb0
     __kmalloc_cache_noprof+0x251/0x610
     snd_jack_kctl_new+0x80/0x180
     snd_jack_new+0x4b/0x490
     snd_hda_jack_add_kctl_mst+0x19b/0x610
     add_jack_kctl+0x204/0x320
     snd_hda_jack_add_kctls+0x212/0x910
     snd_hda_gen_build_controls+0x233/0x600
     snd_hda_codec_build_controls+0x2e4/0x9f0
     hda_codec_driver_probe+0x26e/0x590
     really_probe+0x21f/0xa90
     __driver_probe_device+0x1ec/0x490
     device_driver_attach+0xdc/0x210
     bind_store+0xec/0x180
     drv_attr_store+0x79/0xb0
     sysfs_kf_write+0xed/0x140
     kernfs_fop_write_iter+0x34b/0x500
     vfs_write+0x637/0x1010
     ksys_write+0x111/0x200
     do_syscall_64+0x115/0x6a0
     entry_SYSCALL_64_after_hwframe+0x77/0x7f

    Freed by task 425:
     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_jack_kctl_private_free+0x153/0x200
     __snd_ctl_remove+0x29f/0x3b0
     snd_ctl_remove+0x46/0x70
     snd_jack_dev_free+0x1b1/0x370
     __snd_device_free+0x193/0x3f0
     snd_device_free+0xcf/0x190
     snd_hda_jack_tbl_clear+0x159/0x2e0
     snd_hda_codec_cleanup_for_unbind+0x37e/0x9c0
     hda_codec_driver_probe+0x3ae/0x590
     really_probe+0x21f/0xa90
     __driver_probe_device+0x1ec/0x490
     device_driver_attach+0xdc/0x210
     bind_store+0xec/0x180
     drv_attr_store+0x79/0xb0
     sysfs_kf_write+0xed/0x140
     kernfs_fop_write_iter+0x34b/0x500
     vfs_write+0x637/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 ffff8881085f9d80
     which belongs to the cache kmalloc-64 of size 64
    The buggy address is located 32 bytes inside of
     freed 64-byte region [ffff8881085f9d80, ffff8881085f9dc0)

    The buggy address belongs to the physical page:
    page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1085f9
    flags: 0x200000000000000(node=0|zone=2)
    page_type: f5(slab)
    raw: 0200000000000000 ffff8881000428c0 dead000000000100 dead000000000122
    raw: 0000000000000000 0000000000200020 00000000f5000000 0000000000000000
    page dumped because: kasan: bad access detected

    Memory state around the buggy address:
     ffff8881085f9c80: 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc
     ffff8881085f9d00: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
    >ffff8881085f9d80: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
                                   ^
     ffff8881085f9e00: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
     ffff8881085f9e80: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
    ==================================================================

Fixes: 495000a38634 ("ALSA: core: Remove debugfs at disconnection")
Assisted-by: LLM
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---

diff --git a/sound/core/jack.c b/sound/core/jack.c
index 0f04018181acb20f6bf85b11a9f6d4c7d3179e5a..5cc9c42de9811e525b32935d98befbacaa09d500 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -68,6 +68,9 @@ static int snd_jack_dev_free(struct snd_device *device)
 	struct snd_card *card = device->card;
 	struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl;
 
+	/* Remove debugfs while the controls are still linked. */
+	snd_jack_remove_debugfs(jack);
+
 	list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) {
 		list_del_init(&jack_kctl->list);
 		snd_ctl_remove(card, jack_kctl->kctl);

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] ALSA: jack: Remove debugfs before freeing unregistered jacks
  2026-10-11 15:55 [PATCH] ALSA: jack: Remove debugfs before freeing unregistered jacks Cen Zhang
@ 2026-10-12  7:28 ` Takashi Iwai
  0 siblings, 0 replies; 2+ messages in thread
From: Takashi Iwai @ 2026-10-12  7:28 UTC (permalink / raw)
  To: Cen Zhang
  Cc: perex, tiwai, phucduc.bui, kees, blum, linux-sound, linux-kernel,
	baijiaju1990, jjzuming

On Sun, 11 Oct 2026 17:55:56 +0200,
Cen Zhang wrote:
> 
> Jack debugfs files store a raw snd_jack_kctl pointer, so their
> callbacks must be drained before the attachment is freed.
> snd_jack_dev_free() unlinks and frees the attachments before calling
> snd_jack_dev_disconnect(). Its late snd_jack_remove_debugfs() call
> then has no entries left to traverse.
> 
> With CONFIG_SND_JACK_INJECTION_DEBUG and input support enabled, an
> HDA codec rebind on a live card can publish a non-phantom jack's type
> file while its component is still in SNDRV_DEV_BUILD. A later
> jack-control allocation failure permits the following order:
> 
>   1. hda_codec_driver_probe() creates a jack and publishes its debugfs
>      files before reaching snd_card_register().
>   2. A later control-build failure calls
>      snd_hda_codec_cleanup_for_unbind(), then snd_hda_jack_tbl_clear()
>      and snd_device_free(). __snd_device_disconnect() skips BUILD
>      components. snd_jack_dev_free() frees the attachment before its
>      late disconnect, leaving the debugfs files published.
>   3. After cleanup, a reader opens the surviving type file and
>      jack_type_read() dereferences the freed attachment's jack pointer.
> 
> The card's debugfs root remains live throughout this sequence. The
> result is a use-after-free even without an overlapping read. An
> already admitted callback is also not drained by this cleanup.
> 
> Call snd_jack_remove_debugfs() before unlinking any controls in the
> free callback. The existing debugfs_remove() rundown blocks new
> accesses and waits for active callbacks while the attachment, control
> and jack remain valid. Roots cleared by an earlier disconnect are
> NULL, so repeating the removal is harmless.
> 
> KASAN report as below:
> 
>     ==================================================================
>     BUG: KASAN: slab-use-after-free in jack_type_read+0x1da/0x1f0
>     Read of size 8 at addr ffff8881085f9da0 by task python3/429
> 
>     CPU: 2 UID: 0 PID: 429 Comm: python3 Not tainted 7.2.0-rc6-pmb-alsa-functional-v2+ #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
>      ? jack_type_read+0x1da/0x1f0
>      ? srso_alias_return_thunk+0x5/0xfbef5
>      ? __virt_addr_valid+0x20d/0x410
>      ? jack_type_read+0x1da/0x1f0
>      kasan_report+0xe0/0x110
>      ? jack_type_read+0x1da/0x1f0
>      jack_type_read+0x1da/0x1f0
>      ? __pfx_jack_type_read+0x10/0x10
>      ? __pfx___debugfs_file_get+0x10/0x10
>      ? __pfx_cp_new_stat+0x10/0x10
>      ? srso_alias_return_thunk+0x5/0xfbef5
>      ? srso_alias_return_thunk+0x5/0xfbef5
>      ? selinux_file_permission+0x122/0x570
>      full_proxy_read+0x11f/0x180
>      ? __pfx_full_proxy_read+0x10/0x10
>      vfs_read+0x1a0/0xc20
>      ? __pfx_vfs_read+0x10/0x10
>      ? srso_alias_return_thunk+0x5/0xfbef5
>      ? __do_sys_newfstat+0xa6/0x100
>      ? find_held_lock+0x2b/0x80
>      ? __pfx___do_sys_newfstat+0x10/0x10
>      ksys_read+0x111/0x200
>      ? __pfx_ksys_read+0x10/0x10
>      ? full_proxy_llseek+0x11e/0x170
>      do_syscall_64+0x115/0x6a0
>      entry_SYSCALL_64_after_hwframe+0x77/0x7f
>     RIP: 0033:0x7f7e392e7687
>     Code: 48 89 fa 4c 89 df e8 58 b3 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
>     RSP: 002b:00007fff5f7b9180 EFLAGS: 00000202 ORIG_RAX: 0000000000000000
>     RAX: ffffffffffffffda RBX: 00007f7e39253780 RCX: 00007f7e392e7687
>     RDX: 0000000000002000 RSI: 000000003d2e9b00 RDI: 0000000000000003
>     RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
>     R10: 0000000000000000 R11: 0000000000000202 R12: 00007f7e39253700
>     R13: 0000000000002000 R14: 00007f7e38d7e2c0 R15: 0000000000a55b98
>      </TASK>
> 
>     Allocated by task 425:
>      kasan_save_stack+0x33/0x60
>      kasan_save_track+0x14/0x30
>      __kasan_kmalloc+0xaa/0xb0
>      __kmalloc_cache_noprof+0x251/0x610
>      snd_jack_kctl_new+0x80/0x180
>      snd_jack_new+0x4b/0x490
>      snd_hda_jack_add_kctl_mst+0x19b/0x610
>      add_jack_kctl+0x204/0x320
>      snd_hda_jack_add_kctls+0x212/0x910
>      snd_hda_gen_build_controls+0x233/0x600
>      snd_hda_codec_build_controls+0x2e4/0x9f0
>      hda_codec_driver_probe+0x26e/0x590
>      really_probe+0x21f/0xa90
>      __driver_probe_device+0x1ec/0x490
>      device_driver_attach+0xdc/0x210
>      bind_store+0xec/0x180
>      drv_attr_store+0x79/0xb0
>      sysfs_kf_write+0xed/0x140
>      kernfs_fop_write_iter+0x34b/0x500
>      vfs_write+0x637/0x1010
>      ksys_write+0x111/0x200
>      do_syscall_64+0x115/0x6a0
>      entry_SYSCALL_64_after_hwframe+0x77/0x7f
> 
>     Freed by task 425:
>      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_jack_kctl_private_free+0x153/0x200
>      __snd_ctl_remove+0x29f/0x3b0
>      snd_ctl_remove+0x46/0x70
>      snd_jack_dev_free+0x1b1/0x370
>      __snd_device_free+0x193/0x3f0
>      snd_device_free+0xcf/0x190
>      snd_hda_jack_tbl_clear+0x159/0x2e0
>      snd_hda_codec_cleanup_for_unbind+0x37e/0x9c0
>      hda_codec_driver_probe+0x3ae/0x590
>      really_probe+0x21f/0xa90
>      __driver_probe_device+0x1ec/0x490
>      device_driver_attach+0xdc/0x210
>      bind_store+0xec/0x180
>      drv_attr_store+0x79/0xb0
>      sysfs_kf_write+0xed/0x140
>      kernfs_fop_write_iter+0x34b/0x500
>      vfs_write+0x637/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 ffff8881085f9d80
>      which belongs to the cache kmalloc-64 of size 64
>     The buggy address is located 32 bytes inside of
>      freed 64-byte region [ffff8881085f9d80, ffff8881085f9dc0)
> 
>     The buggy address belongs to the physical page:
>     page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1085f9
>     flags: 0x200000000000000(node=0|zone=2)
>     page_type: f5(slab)
>     raw: 0200000000000000 ffff8881000428c0 dead000000000100 dead000000000122
>     raw: 0000000000000000 0000000000200020 00000000f5000000 0000000000000000
>     page dumped because: kasan: bad access detected
> 
>     Memory state around the buggy address:
>      ffff8881085f9c80: 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc
>      ffff8881085f9d00: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
>     >ffff8881085f9d80: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
>                                    ^
>      ffff8881085f9e00: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
>      ffff8881085f9e80: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
>     ==================================================================
> 
> Fixes: 495000a38634 ("ALSA: core: Remove debugfs at disconnection")
> Assisted-by: LLM
> Signed-off-by: Cen Zhang <zzzccc427@gmail.com>

Could you verify whether the problem still exists with the latest
sound.git tree for-next branch (i.e. for 7.4)?  There have been fixes
for the races at the disconnection, and snd_card_disconnect() became
already mandatory before the release.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-12  7:28 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-11 15:55 [PATCH] ALSA: jack: Remove debugfs before freeing unregistered jacks Cen Zhang
2026-10-12  7:28 ` Takashi Iwai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®