* Fwd: [BUG] HID: sensor: custom: use-after-free of sensor_inst via open misc device after remove (hard lockup) [not found] <CAM2dg7eZJEii+fJT1TG8XME-TJFqDtURCsr5XS71E5-5-mUV0A@mail.gmail.com> @ 2026-10-23 10:42 ` Семён Марьясин 2026-10-23 14:12 ` Srinivas Pandruvada 0 siblings, 1 reply; 3+ messages in thread From: Семён Марьясин @ 2026-10-23 10:42 UTC (permalink / raw) To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada Cc: Benjamin Tissoires, Haoxiang Li, linux-input, linux-iio [-- Attachment #1: Type: text/plain, Size: 5585 bytes --] Hello, hid-sensor-custom frees its per-sensor state (sensor_inst, devm-allocated) on remove while userspace can still hold the misc device /dev/HID-SENSOR-2000e1.N.auto open. A reader blocked in read() stays queued on sensor_inst->wait in freed memory; when it wakes up, finish_wait() spins on a garbage wait-queue lock with interrupts disabled and the machine hard-locks. Any read()/poll()/release() on such an fd after remove is a use-after-free as well. The Sashiko AI review already flagged this as a pre-existing issue on Haoxiang Li's "Fix fields lifetime issues" series ("The /dev/HID-SENSOR-* character device operations do not synchronize with device removal, leading to Use-After-Free on devm-managed structures"): https://lore.kernel.org/linux-input/20260707071545.3087073-1-haoxiang_li2024@163.com/ The applied patches don't touch it, and the current mainline is unchanged. Below is a real-world hit. Kernel: 7.2.8 (NixOS; drivers/hid/hid-sensor-custom.c is identical to current mainline, including ad8fb82b0442 and 3789d0802ddb) Hardware: Dell Latitude 5420 (Tiger Lake, Intel ISH), BIOS 1.49.0; Dell's vendor ISH sensors appear as HID-SENSOR-2000e1.N.auto (e.g. an ST VL53L1 time-of-flight sensor). Taint: O, from unrelated out-of-tree WWAN modules (qcserial, usb_wwan). How it was hit -------------- 1. echo 1 > /sys/bus/platform/devices/HID-SENSOR-2000e1.2.auto/enable_sensor 2. xxd /dev/HID-SENSOR-2000e1.2.auto # streaming reader, left running 3. modprobe -r intel_ishtp_hid intel_ish_ipc && modprobe intel_ish_ipc The hub went away and was re-enumerated fine; ~30 s later CPU0 hard-locked in the still-running xxd and the hard-lockup detector panicked the machine (log recovered from EFI pstore). Presumably a minimal reproducer is just a blocked reader plus unbinding the platform device (echo HID-SENSOR-2000e1.N.auto > /sys/bus/platform/drivers/hid_sensor_custom/unbind), but only the module-reload path above was actually tested so far. Log (trimmed) ------------- [146608.877730] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting for response from ISHTP device [146611.885959] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting for response from ISHTP device [146614.893909] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting for response from ISHTP device [146617.901872] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting for response from ISHTP device [146619.972550] ish-hid {33AECD58-B679-4E54-9BD9-A04D34F0C226}: [hid-ish]: enum_devices_done OK, num_hid_devices=3 [146620.031441] hid-sensor-hub 001F:8087:0AC2.0007: hidraw3: SENSOR HUB HID v2.00 Device [hid-ishtp 8087:0AC2] on [146643.503266] iwlwifi 0000:00:14.3: Queue 3 is stuck 46 70 [146648.924391] watchdog: CPU0: Watchdog detected hard LOCKUP on cpu 0 [146648.924796] CPU: 0 UID: 0 PID: 1316004 Comm: xxd Tainted: G O 7.2.8 #1-NixOS PREEMPT(lazy) [146648.924804] Hardware name: Dell Inc. Latitude 5420/03P5X2, BIOS 1.49.0 09/01/2025 [146648.924808] RIP: 0010:native_queued_spin_lock_slowpath+0x64/0x2c0 [146648.924851] Call Trace: [146648.924855] <TASK> [146648.924857] _raw_spin_lock_irqsave+0x3d/0x50 [146648.924872] finish_wait+0x3b/0x90 [146648.924886] hid_sensor_custom_read+0x102/0x150 [hid_sensor_custom] [146648.924901] vfs_read+0xbf/0x390 [146648.924934] ksys_read+0xbf/0xf0 [146648.924939] do_syscall_64+0x134/0x6b0 [146648.925010] entry_SYSCALL_64_after_hwframe+0x77/0x7f [146648.925083] </TASK> [146648.925086] Kernel panic - not syncing: Hard LOCKUP Also attaching a full dmesg log (with only wi-fi noise filtered out). Analysis (line numbers as in mainline 73bef5) --------------------------------------------- - hid_sensor_custom_probe() allocates sensor_inst with devm_kzalloc() (line 980), so it is freed right after hid_sensor_custom_remove() returns, regardless of open files. - The misc device's fops reach sensor_inst through container_of(file->private_data, ..., custom_dev); misc_deregister() does not revoke already-open files, so read/poll/release keep dereferencing it. - hid_sensor_custom_dev_if_remove() (lines 751-758) does wake_up(), misc_deregister(), kfifo_free(). The woken reader in hid_sensor_custom_read() (line 664) re-evaluates wait_event_interruptible(sensor_inst->wait, !kfifo_is_empty(&sensor_inst->data_fifo)), finds the fifo empty and goes back to sleep on sensor_inst->wait. Nothing tells it the device is gone. - Once sensor_inst is freed and reused, the next wakeup of that reader runs finish_wait() on the freed wait_queue_head: spin_lock_irqsave() on garbage, hard lockup. hid_sensor_custom_release() similarly does clear_bit() on freed sensor_inst->misc_opened when the fd is finally closed. Possible fix direction ---------------------- Tie sensor_inst's lifetime to its users rather than to devres, the way hidraw handles disconnect: refcount it (kref: one reference for the device, one per open file; kfifo_free() and kfree() on the last put), add a "removed" flag set in remove() before wake_up_interruptible_all(), include it in the wait_event condition, and make read()/poll() return -ENODEV / EPOLLHUP | EPOLLERR once removed. The same review pointed out two more problems in this teardown path that a fix would naturally cover: remove() frees data_fifo before sensor_hub_remove_callback(), so a sample arriving in between is kfifo_in()'d into freed memory; and an enabled sensor is never sensor_hub_device_close()d on remove. I can test patches on this machine. Thanks, Semyon Maryasin P.S.: sorry for a possible duplicate, I think I messed with the headers. [-- Attachment #2: dmesg-clean.txt --] [-- Type: text/plain, Size: 14021 bytes --] <6>[122586.929775] PM: suspend entry (s2idle) <6>[122586.977820] Filesystems sync: 0.048 seconds <6>[122586.988535] Freezing user space processes <6>[122586.990269] Freezing user space processes completed (elapsed 0.001 seconds) <6>[122586.990274] OOM killer disabled. <6>[122586.990275] Freezing remaining freezable tasks <6>[122586.991832] Freezing remaining freezable tasks completed (elapsed 0.001 seconds) <6>[122586.991835] printk: Suspending console(s) (use no_console_suspend to debug) <6>[122587.012388] e1000e: EEE TX LPI TIMER: 00000011 <6>[122587.394462] ACPI: EC: interrupt blocked <6>[122590.828113] ACPI: EC: interrupt unblocked <6>[122591.185256] i915 0000:00:02.0: vgaarb: VGA decodes changed: olddecodes=io,decodes=io:owns=io+mem <6>[122591.205074] nvme nvme0: 8/0/0 default/read/poll queues <6>[122591.207053] nvme nvme0: Ignoring bogus Namespace Identifiers <6>[122592.245524] OOM killer enabled. <6>[122592.245530] Restarting tasks: Starting <6>[122592.248615] mei_hdcp 0000:00:16.0-b638ab7e-94e2-4ea2-a552-d1c54b627f04: bound 0000:00:02.0 (ops i915_hdcp_ops [i915]) <6>[122592.251481] Restarting tasks: Done <6>[122592.251488] efivarfs: resyncing variable state <6>[122592.259633] efivarfs: finished resyncing variable state <5>[122592.259666] random: crng reseeded on system resumption <6>[122592.294055] PM: suspend exit <6>[122592.392682] e1000e 0000:00:1f.6 eth0: NIC Link is Down <6>[122622.964242] PM: suspend entry (s2idle) <6>[122623.008566] Filesystems sync: 0.044 seconds <6>[122623.023288] Freezing user space processes <6>[122623.025388] Freezing user space processes completed (elapsed 0.002 seconds) <6>[122623.025393] OOM killer disabled. <6>[122623.025394] Freezing remaining freezable tasks <6>[122623.027011] Freezing remaining freezable tasks completed (elapsed 0.001 seconds) <6>[122623.027014] printk: Suspending console(s) (use no_console_suspend to debug) <6>[122623.045649] e1000e: EEE TX LPI TIMER: 00000011 <6>[122623.426308] ACPI: EC: interrupt blocked <6>[142890.179053] ACPI: EC: interrupt unblocked <6>[142890.577373] i915 0000:00:02.0: vgaarb: VGA decodes changed: olddecodes=io,decodes=io:owns=io+mem <6>[142890.602715] nvme nvme0: 8/0/0 default/read/poll queues <6>[142890.604761] nvme nvme0: Ignoring bogus Namespace Identifiers <6>[142891.665519] OOM killer enabled. <6>[142891.665532] Restarting tasks: Starting <6>[142891.669205] mei_hdcp 0000:00:16.0-b638ab7e-94e2-4ea2-a552-d1c54b627f04: bound 0000:00:02.0 (ops i915_hdcp_ops [i915]) <6>[142891.681315] Restarting tasks: Done <6>[142891.681353] efivarfs: resyncing variable state <6>[142891.727579] efivarfs: finished resyncing variable state <5>[142891.727685] random: crng reseeded on system resumption <6>[142891.870922] PM: suspend exit <6>[142892.153878] e1000e 0000:00:1f.6 eth0: NIC Link is Down <3>[146608.877730] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting for response from ISHTP device <3>[146611.885959] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting for response from ISHTP device <3>[146614.893909] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting for response from ISHTP device <3>[146617.901872] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting for response from ISHTP device <6>[146619.972550] ish-hid {33AECD58-B679-4E54-9BD9-A04D34F0C226}: [hid-ish]: enum_devices_done OK, num_hid_devices=3 <6>[146619.981751] hid-sensor-hub 001F:8087:0AC2.0005: hidraw0: SENSOR HUB HID v2.00 Device [hid-ishtp 8087:0AC2] on <6>[146620.011396] hid-sensor-hub 001F:8087:0AC2.0006: hidraw1: SENSOR HUB HID v2.00 Device [hid-ishtp 8087:0AC2] on <6>[146620.031441] hid-sensor-hub 001F:8087:0AC2.0007: hidraw3: SENSOR HUB HID v2.00 Device [hid-ishtp 8087:0AC2] on <0>[146648.924391] watchdog: CPU0: Watchdog detected hard LOCKUP on cpu 0 <4>[146648.924399] Modules linked in: intel_ishtp_hid intel_ish_ipc intel_ishtp ext4 mbcache jbd2 sd_mod uas usb_storage scsi_mod scsi_common nf_conntrack_netlink unix_diag ccm tcp_diag inet_diag qrtr rfcomm snd_seq_dummy snd_hrtimer snd_seq snd_seq_device xfrm_user xfrm_algo xt_set ip_set xt_addrtype overlay xt_CHECKSUM xt_MASQUERADE af_packet aes algif_hash algif_skcipher af_alg uinput wireguard libcurve25519 bnep libchacha20poly1305 libchacha libpoly1305 snd_hda_codec_intelhdmi ip6_udp_tunnel snd_hda_codec_hdmi udp_tunnel msr snd_ctl_led dell_pc snd_hda_codec_alc269 snd_hda_codec_realtek_lib snd_hda_scodec_component snd_hda_codec_generic hid_sensor_custom_intel_hinge snd_hda_intel snd_sof_pci_intel_tgl snd_sof_pci_intel_cnl snd_sof_intel_hda_generic soundwire_intel hid_sensor_gyro_3d hid_sensor_trigger snd_sof_intel_hda_sdw_bpt kfifo_buf hid_sensor_iio_common snd_sof_intel_hda_common industrialio snd_soc_hdac_hda hid_sensor_custom snd_sof_intel_hda_mlink snd_sof_intel_hda soundwire_cadence snd_sof_pci snd_sof_xtensa_dsp <4>[146648.924491] snd_sof joydev snd_sof_utils snd_soc_acpi_intel_match snd_soc_acpi_intel_sdca_quirks soundwire_generic_allocation hid_sensor_hub snd_soc_sdw_utils snd_soc_acpi soundwire_bus mousedev nls_iso8859_1 snd_soc_sdca nls_cp437 ofpart vfat crc8 cmdlinepart ee1004 spi_nor hid_multitouch snd_soc_avs iTCO_wdt mtd hid_generic intel_pmc_bxt snd_soc_hda_codec snd_hda_ext_core snd_hda_codec intel_rapl_msr snd_hda_core uvcvideo snd_intel_dspcfg intel_uncore_frequency intel_uncore_frequency_common snd_intel_sdw_acpi videobuf2_vmalloc snd_hwdep uvc x86_pkg_temp_thermal intel_powerclamp cdc_mbim snd_soc_core videobuf2_memops cdc_wdm videobuf2_v4l2 cdc_ncm cdc_ether videobuf2_common snd_compress iwlmvm ac97_bus snd_pcm_dmaengine dell_rbtn videodev coretemp snd_pcm usbnet dell_wmi mei_hdcp mei_pxp mei_wdt mc mii aesni_intel dell_laptop gf128mul mac80211 e1000e fat snd_timer i2c_i801 rapl dell_smbios ucsi_acpi snd ptp typec_ucsi btusb dell_wmi_sysman i2c_smbus intel_cstate dcdbas spi_intel_pci libarc4 xe intel_uncore psmouse <4>[146648.924696] int340x_thermal_zone 8250_pci edac_core virt_dma intel_soc_dts_iosf rfkill evdev mac_hid ecc crc16 serio_raw xt_conntrack xt_tcpudp ip6t_rpfilter ipt_rpfilter xt_pkttype nft_compat nf_tables sch_fq_codel kvm_intel kvm xt_nat x_tables nf_nat br_netfilter nf_conntrack bridge qcserial(O) nf_defrag_ipv6 nf_defrag_ipv4 usb_wwan(O) tun veth stp fuse loop llc irqbypass cpufreq_powersave ctr dmi_sysfs nvme atkbd rtsx_pci_sdmmc libps2 nvme_core mmc_core vivaldi_fmap xhci_pci i8042 nvme_keyring rtsx_pci nvme_auth thunderbolt serio xhci_hcd btrfs i915 libblake2b drm_buddy i2c_algo_bit video wmi ttm intel_gtt drm_display_helper dm_mod raid6_pq cec xor configfs efivarfs autofs4 [last unloaded: intel_ishtp] <4>[146648.924796] CPU: 0 UID: 0 PID: 1316004 Comm: xxd Tainted: G O 7.2.8 #1-NixOS PREEMPT(lazy) <4>[146648.924803] Tainted: [O]=OOT_MODULE <4>[146648.924804] Hardware name: Dell Inc. Latitude 5420/03P5X2, BIOS 1.49.0 09/01/2025 <4>[146648.924808] RIP: 0010:native_queued_spin_lock_slowpath+0x64/0x2c0 <4>[146648.924821] Code: 77 71 f0 0f ba 29 08 0f 92 c2 8b 01 0f b6 d2 c1 e2 08 30 e4 09 d0 3d ff 00 00 00 77 4d 85 c0 74 10 0f b6 01 84 c0 74 09 f3 90 <0f> b6 01 84 c0 75 f7 b8 01 00 00 00 66 89 01 e9 98 1b 00 00 8b 37 <4>[146648.924824] RSP: 0018:ffffd1d80fbbfa80 EFLAGS: 00000002 <4>[146648.924828] RAX: 0000000000000001 RBX: 0000000000000202 RCX: ffff8eb6c9f17f28 <4>[146648.924831] RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff8eb6c9f17f28 <4>[146648.924833] RBP: ffffd1d80fbbfae0 R08: 0000000000000001 R09: 0000000000000001 <4>[146648.924836] R10: ffff8ec019080000 R11: 0000000000000000 R12: ffff8eb6c9f17f28 <4>[146648.924838] R13: ffff8ec1b5d03180 R14: 0000000000000001 R15: 0000000000000000 <4>[146648.924840] FS: 00007e18ed70a780(0000) GS:ffff8ec66aeb9000(0000) knlGS:0000000000000000 <4>[146648.924844] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 <4>[146648.924846] CR2: 0000742747faf000 CR3: 0000000bf305f004 CR4: 0000000000f72ef0 <4>[146648.924849] PKRU: 55555554 <4>[146648.924851] Call Trace: <4>[146648.924855] <TASK> <4>[146648.924857] _raw_spin_lock_irqsave+0x3d/0x50 <4>[146648.924872] finish_wait+0x3b/0x90 <4>[146648.924886] hid_sensor_custom_read+0x102/0x150 [hid_sensor_custom] <4>[146648.924894] ? __pfx_autoremove_wake_function+0x10/0x10 <4>[146648.924901] vfs_read+0xbf/0x390 <4>[146648.924910] ? tty_insert_flip_string_and_push_buffer+0x95/0xc0 <4>[146648.924921] ? remove_wait_queue+0x24/0x60 <4>[146648.924926] ? n_tty_write+0x3b9/0x4e0 <4>[146648.924934] ksys_read+0xbf/0xf0 <4>[146648.924939] do_syscall_64+0x134/0x6b0 <4>[146648.924946] ? file_tty_write.isra.0+0x20e/0x320 <4>[146648.924952] ? vfs_write+0x25d/0x470 <4>[146648.924958] ? ksys_write+0x73/0xf0 <4>[146648.924963] ? do_syscall_64+0x173/0x6b0 <4>[146648.924966] ? n_tty_write+0x3b9/0x4e0 <4>[146648.924972] ? __wake_up+0x44/0x60 <4>[146648.924978] ? file_tty_write.isra.0+0x20e/0x320 <4>[146648.924983] ? vfs_write+0x25d/0x470 <4>[146648.924989] ? ksys_write+0x73/0xf0 <4>[146648.924993] ? do_syscall_64+0x173/0x6b0 <4>[146648.924997] ? do_syscall_64+0x173/0x6b0 <4>[146648.925000] ? do_syscall_64+0xeb/0x6b0 <4>[146648.925002] ? clear_bhb_loop+0x60/0xb0 <4>[146648.925010] entry_SYSCALL_64_after_hwframe+0x77/0x7f <4>[146648.925016] RIP: 0033:0x7e18ed49915e <4>[146648.925059] Code: 03 00 00 59 5e 48 83 f8 fc 74 39 c9 31 d2 31 c9 31 f6 31 ff 45 31 c0 45 31 c9 45 31 d2 45 31 db c3 0f 1f 00 48 8b 45 10 0f 05 <c9> 31 d2 31 c9 31 f6 31 ff 45 31 c0 45 31 c9 45 31 d2 45 31 db c3 <4>[146648.925062] RSP: 002b:00007ffd67ba5f60 EFLAGS: 00000202 ORIG_RAX: 0000000000000000 <4>[146648.925065] RAX: ffffffffffffffda RBX: 0000614264f63310 RCX: 00007e18ed49915e <4>[146648.925067] RDX: 0000000000001000 RSI: 0000614264f634f0 RDI: 0000000000000003 <4>[146648.925069] RBP: 00007ffd67ba5f70 R08: 0000000000000000 R09: 0000000000000000 <4>[146648.925071] R10: 0000000000000000 R11: 0000000000000202 R12: 00007e18ed5f9010 <4>[146648.925073] R13: 00007e18ed5f8ec0 R14: 0000000000000000 R15: 0000000000000000 <4>[146648.925083] </TASK> <0>[146648.925086] Kernel panic - not syncing: Hard LOCKUP <4>[146648.925089] CPU: 0 UID: 0 PID: 1316004 Comm: xxd Tainted: G O 7.2.8 #1-NixOS PREEMPT(lazy) <4>[146648.925093] Tainted: [O]=OOT_MODULE <4>[146648.925094] Hardware name: Dell Inc. Latitude 5420/03P5X2, BIOS 1.49.0 09/01/2025 <4>[146648.925096] Call Trace: <4>[146648.925099] <NMI> <4>[146648.925101] dump_stack_lvl+0x5d/0x80 <4>[146648.925110] vpanic+0x20f/0x410 <4>[146648.925118] panic+0x6b/0x70 <4>[146648.925124] nmi_panic.cold+0xc/0xc <4>[146648.925129] watchdog_hardlockup_check.cold+0x11e/0x1e8 <4>[146648.925136] __perf_event_overflow+0x120/0x3c0 <4>[146648.925148] handle_pmi_common+0x1e0/0x450 <4>[146648.925162] intel_pmu_handle_irq+0xf4/0x4c0 <4>[146648.925167] perf_event_nmi_handler+0x42/0x70 <4>[146648.925176] nmi_handle.part.0+0x53/0x110 <4>[146648.925185] default_do_nmi+0x10e/0x170 <4>[146648.925193] exc_nmi+0x129/0x1a0 <4>[146648.925198] end_repeat_nmi+0xf/0x53 <4>[146648.925203] RIP: 0010:native_queued_spin_lock_slowpath+0x64/0x2c0 <4>[146648.925207] Code: 77 71 f0 0f ba 29 08 0f 92 c2 8b 01 0f b6 d2 c1 e2 08 30 e4 09 d0 3d ff 00 00 00 77 4d 85 c0 74 10 0f b6 01 84 c0 74 09 f3 90 <0f> b6 01 84 c0 75 f7 b8 01 00 00 00 66 89 01 e9 98 1b 00 00 8b 37 <4>[146648.925210] RSP: 0018:ffffd1d80fbbfa80 EFLAGS: 00000002 <4>[146648.925213] RAX: 0000000000000001 RBX: 0000000000000202 RCX: ffff8eb6c9f17f28 <4>[146648.925215] RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff8eb6c9f17f28 <4>[146648.925217] RBP: ffffd1d80fbbfae0 R08: 0000000000000001 R09: 0000000000000001 <4>[146648.925219] R10: ffff8ec019080000 R11: 0000000000000000 R12: ffff8eb6c9f17f28 <4>[146648.925221] R13: ffff8ec1b5d03180 R14: 0000000000000001 R15: 0000000000000000 <4>[146648.925227] ? native_queued_spin_lock_slowpath+0x64/0x2c0 <4>[146648.925231] ? native_queued_spin_lock_slowpath+0x64/0x2c0 <4>[146648.925235] </NMI> <4>[146648.925236] <TASK> <4>[146648.925238] _raw_spin_lock_irqsave+0x3d/0x50 <4>[146648.925243] finish_wait+0x3b/0x90 <4>[146648.925249] hid_sensor_custom_read+0x102/0x150 [hid_sensor_custom] <4>[146648.925254] ? __pfx_autoremove_wake_function+0x10/0x10 <4>[146648.925261] vfs_read+0xbf/0x390 <4>[146648.925264] ? tty_insert_flip_string_and_push_buffer+0x95/0xc0 <4>[146648.925271] ? remove_wait_queue+0x24/0x60 <4>[146648.925276] ? n_tty_write+0x3b9/0x4e0 <4>[146648.925281] ksys_read+0xbf/0xf0 <4>[146648.925285] do_syscall_64+0x134/0x6b0 <4>[146648.925289] ? file_tty_write.isra.0+0x20e/0x320 <4>[146648.925294] ? vfs_write+0x25d/0x470 <4>[146648.925300] ? ksys_write+0x73/0xf0 <4>[146648.925304] ? do_syscall_64+0x173/0x6b0 <4>[146648.925308] ? n_tty_write+0x3b9/0x4e0 <4>[146648.925313] ? __wake_up+0x44/0x60 <4>[146648.925319] ? file_tty_write.isra.0+0x20e/0x320 <4>[146648.925323] ? vfs_write+0x25d/0x470 <4>[146648.925330] ? ksys_write+0x73/0xf0 <4>[146648.925334] ? do_syscall_64+0x173/0x6b0 <4>[146648.925337] ? do_syscall_64+0x173/0x6b0 <4>[146648.925340] ? do_syscall_64+0xeb/0x6b0 <4>[146648.925343] ? clear_bhb_loop+0x60/0xb0 <4>[146648.925348] entry_SYSCALL_64_after_hwframe+0x77/0x7f <4>[146648.925352] RIP: 0033:0x7e18ed49915e <4>[146648.925359] Code: 03 00 00 59 5e 48 83 f8 fc 74 39 c9 31 d2 31 c9 31 f6 31 ff 45 31 c0 45 31 c9 45 31 d2 45 31 db c3 0f 1f 00 48 8b 45 10 0f 05 <c9> 31 d2 31 c9 31 f6 31 ff 45 31 c0 45 31 c9 45 31 d2 45 31 db c3 <4>[146648.925361] RSP: 002b:00007ffd67ba5f60 EFLAGS: 00000202 ORIG_RAX: 0000000000000000 <4>[146648.925365] RAX: ffffffffffffffda RBX: 0000614264f63310 RCX: 00007e18ed49915e <4>[146648.925367] RDX: 0000000000001000 RSI: 0000614264f634f0 RDI: 0000000000000003 <4>[146648.925369] RBP: 00007ffd67ba5f70 R08: 0000000000000000 R09: 0000000000000000 <4>[146648.925371] R10: 0000000000000000 R11: 0000000000000202 R12: 00007e18ed5f9010 <4>[146648.925373] R13: 00007e18ed5f8ec0 R14: 0000000000000000 R15: 0000000000000000 <4>[146648.925379] </TASK> <0>[146648.925436] Kernel Offset: 0x20c00000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff) ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Fwd: [BUG] HID: sensor: custom: use-after-free of sensor_inst via open misc device after remove (hard lockup) 2026-10-23 10:42 ` Fwd: [BUG] HID: sensor: custom: use-after-free of sensor_inst via open misc device after remove (hard lockup) Семён Марьясин @ 2026-10-23 14:12 ` Srinivas Pandruvada 2026-10-27 12:36 ` Семён Марьясин 0 siblings, 1 reply; 3+ messages in thread From: Srinivas Pandruvada @ 2026-10-23 14:12 UTC (permalink / raw) To: Семён Марьясин, Jiri Kosina, Jonathan Cameron Cc: Benjamin Tissoires, Haoxiang Li, linux-input, linux-iio [-- Attachment #1: Type: text/plain, Size: 6221 bytes --] Hi, On Fri, 2026-10-23 at 13:42 +0300, Семён Марьясин wrote: > Hello, > > hid-sensor-custom frees its per-sensor state (sensor_inst, > devm-allocated) on remove while userspace > can still hold the misc device /dev/HID-SENSOR-2000e1.N.auto open. A > reader blocked in read() stays > queued on sensor_inst->wait in freed memory; when it wakes up, > finish_wait() spins on a garbage > wait-queue lock with interrupts disabled and the machine hard-locks. > Any read()/poll()/release() on such > an fd after remove is a use-after-free as well. > Please give a try with the attached diff. Thanks, Srinivas > The Sashiko AI review already flagged this as a pre-existing issue > on Haoxiang Li's "Fix fields lifetime issues" series > ("The /dev/HID-SENSOR-* character device operations do not > synchronize > with device > removal, > leading to Use-After-Free on devm-managed structures"): > https://lore.kernel.org/linux-input/20260707071545.3087073-1-haoxiang_li2024@163.com/ > The applied patches don't touch it, and the current mainline is > unchanged. Below is a real-world hit. > > Kernel: 7.2.8 (NixOS; drivers/hid/hid-sensor-custom.c is identical to > current mainline, > including ad8fb82b0442 and 3789d0802ddb) > Hardware: Dell Latitude 5420 (Tiger Lake, Intel ISH), BIOS 1.49.0; > Dell's vendor ISH sensors appear as > HID-SENSOR-2000e1.N.auto (e.g. an ST VL53L1 time-of-flight > sensor). > Taint: O, from unrelated out-of-tree WWAN modules (qcserial, > usb_wwan). > > How it was hit > -------------- > > 1. echo 1 > /sys/bus/platform/devices/HID-SENSOR- > 2000e1.2.auto/enable_sensor > 2. xxd /dev/HID-SENSOR-2000e1.2.auto # streaming reader, left > running > 3. modprobe -r intel_ishtp_hid intel_ish_ipc && modprobe > intel_ish_ipc > > The hub went away and was re-enumerated fine; ~30 s later CPU0 > hard-locked in the still-running xxd > and the hard-lockup detector panicked the machine (log recovered from > EFI pstore). > > Presumably a minimal reproducer is just a blocked reader plus > unbinding the platform device > (echo HID-SENSOR-2000e1.N.auto > > /sys/bus/platform/drivers/hid_sensor_custom/unbind), > but only the module-reload path above was actually tested so far. > > Log (trimmed) > ------------- > > [146608.877730] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting > for response from ISHTP device > [146611.885959] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting > for response from ISHTP device > [146614.893909] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting > for response from ISHTP device > [146617.901872] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting > for response from ISHTP device > [146619.972550] ish-hid {33AECD58-B679-4E54-9BD9-A04D34F0C226}: > [hid-ish]: enum_devices_done OK, num_hid_devices=3 > [146620.031441] hid-sensor-hub 001F:8087:0AC2.0007: hidraw3: SENSOR > HUB HID v2.00 Device [hid-ishtp 8087:0AC2] on > [146643.503266] iwlwifi 0000:00:14.3: Queue 3 is stuck 46 70 > [146648.924391] watchdog: CPU0: Watchdog detected hard LOCKUP on cpu > 0 > [146648.924796] CPU: 0 UID: 0 PID: 1316004 Comm: xxd Tainted: G > O 7.2.8 #1-NixOS PREEMPT(lazy) > [146648.924804] Hardware name: Dell Inc. Latitude 5420/03P5X2, BIOS > 1.49.0 09/01/2025 > [146648.924808] RIP: 0010:native_queued_spin_lock_slowpath+0x64/0x2c0 > [146648.924851] Call Trace: > [146648.924855] <TASK> > [146648.924857] _raw_spin_lock_irqsave+0x3d/0x50 > [146648.924872] finish_wait+0x3b/0x90 > [146648.924886] hid_sensor_custom_read+0x102/0x150 > [hid_sensor_custom] > [146648.924901] vfs_read+0xbf/0x390 > [146648.924934] ksys_read+0xbf/0xf0 > [146648.924939] do_syscall_64+0x134/0x6b0 > [146648.925010] entry_SYSCALL_64_after_hwframe+0x77/0x7f > [146648.925083] </TASK> > [146648.925086] Kernel panic - not syncing: Hard LOCKUP > > Also attaching a full dmesg log (with only wi-fi noise filtered out). > > Analysis (line numbers as in mainline 73bef5) > --------------------------------------------- > > - hid_sensor_custom_probe() allocates sensor_inst with devm_kzalloc() > (line 980), so it is freed right > after hid_sensor_custom_remove() returns, regardless of open files. > - The misc device's fops reach sensor_inst through > container_of(file->private_data, ..., custom_dev); > misc_deregister() does not revoke already-open files, so > read/poll/release keep dereferencing it. > - hid_sensor_custom_dev_if_remove() (lines 751-758) does wake_up(), > misc_deregister(), kfifo_free(). > The woken reader in hid_sensor_custom_read() (line 664) re- > evaluates > wait_event_interruptible(sensor_inst->wait, > !kfifo_is_empty(&sensor_inst->data_fifo)), finds the fifo > empty and goes back to sleep on sensor_inst->wait. Nothing tells it > the device is gone. > - Once sensor_inst is freed and reused, the next wakeup of that > reader > runs finish_wait() on the freed > wait_queue_head: spin_lock_irqsave() on garbage, hard lockup. > hid_sensor_custom_release() similarly > does clear_bit() on freed sensor_inst->misc_opened when the fd is > finally closed. > > Possible fix direction > ---------------------- > > Tie sensor_inst's lifetime to its users rather than to devres, the > way > hidraw handles disconnect: > refcount it (kref: one reference for the device, one per open file; > kfifo_free() and kfree() on the last > put), add a "removed" flag set in remove() before > wake_up_interruptible_all(), include it in the > wait_event condition, and make read()/poll() return -ENODEV / > EPOLLHUP > > EPOLLERR once removed. > > The same review pointed out two more problems in this teardown path > that a fix would naturally cover: > remove() frees data_fifo before sensor_hub_remove_callback(), so a > sample arriving in between is > kfifo_in()'d into freed memory; and an enabled sensor is never > sensor_hub_device_close()d on remove. > > I can test patches on this machine. > > Thanks, > Semyon Maryasin > > P.S.: sorry for a possible duplicate, I think I messed with the > headers. [-- Attachment #2: test_patch.diff --] [-- Type: text/x-patch, Size: 7630 bytes --] diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c index afffea894021..c12417cddab4 100644 --- a/drivers/hid/hid-sensor-custom.c +++ b/drivers/hid/hid-sensor-custom.c @@ -11,6 +11,7 @@ #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/kfifo.h> +#include <linux/kref.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/poll.h> @@ -37,6 +38,8 @@ struct hid_sensor_custom_field { struct hid_sensor_custom { struct mutex mutex; + struct kref ref; + bool removed; struct platform_device *pdev; struct hid_sensor_hub_device *hsdev; struct hid_sensor_hub_callbacks callbacks; @@ -636,6 +639,15 @@ static void hid_sensor_custom_remove_attributes(struct hid_sensor_custom * kfree(sensor_inst->fields); } +static void hid_sensor_custom_free(struct kref *ref) +{ + struct hid_sensor_custom *sensor_inst = + container_of(ref, struct hid_sensor_custom, ref); + + kfifo_free(&sensor_inst->data_fifo); + kfree(sensor_inst); +} + static ssize_t hid_sensor_custom_read(struct file *file, char __user *buf, size_t count, loff_t *f_ps) { @@ -649,22 +661,35 @@ static ssize_t hid_sensor_custom_read(struct file *file, char __user *buf, if (count < sizeof(struct hid_sensor_sample)) return -EINVAL; - do { - if (kfifo_is_empty(&sensor_inst->data_fifo)) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + for (;;) { + ret = mutex_lock_interruptible(&sensor_inst->mutex); + if (ret) + return ret; - ret = wait_event_interruptible(sensor_inst->wait, - !kfifo_is_empty(&sensor_inst->data_fifo)); - if (ret) - return ret; + if (sensor_inst->removed) { + ret = -ENODEV; + goto unlock; } ret = kfifo_to_user(&sensor_inst->data_fifo, buf, count, &copied); + if (ret || copied) + goto unlock; + mutex_unlock(&sensor_inst->mutex); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(sensor_inst->wait, + READ_ONCE(sensor_inst->removed) || + !kfifo_is_empty(&sensor_inst->data_fifo)); if (ret) return ret; + } - } while (copied == 0); +unlock: + mutex_unlock(&sensor_inst->mutex); + if (ret) + return ret; return copied; } @@ -677,6 +702,7 @@ static int hid_sensor_custom_release(struct inode *inode, struct file *file) struct hid_sensor_custom, custom_dev); clear_bit(0, &sensor_inst->misc_opened); + kref_put(&sensor_inst->ref, hid_sensor_custom_free); return 0; } @@ -684,14 +710,22 @@ static int hid_sensor_custom_release(struct inode *inode, struct file *file) static int hid_sensor_custom_open(struct inode *inode, struct file *file) { struct hid_sensor_custom *sensor_inst; + int ret; sensor_inst = container_of(file->private_data, struct hid_sensor_custom, custom_dev); - /* We essentially have single reader and writer */ - if (test_and_set_bit(0, &sensor_inst->misc_opened)) - return -EBUSY; + mutex_lock(&sensor_inst->mutex); + if (sensor_inst->removed) { + ret = -ENODEV; + } else if (test_and_set_bit(0, &sensor_inst->misc_opened)) { + ret = -EBUSY; + } else { + kref_get(&sensor_inst->ref); + ret = stream_open(inode, file); + } + mutex_unlock(&sensor_inst->mutex); - return stream_open(inode, file); + return ret; } static __poll_t hid_sensor_custom_poll(struct file *file, @@ -705,6 +739,9 @@ static __poll_t hid_sensor_custom_poll(struct file *file, poll_wait(file, &sensor_inst->wait, wait); + if (READ_ONCE(sensor_inst->removed)) + return EPOLLHUP | EPOLLERR; + if (!kfifo_is_empty(&sensor_inst->data_fifo)) mask = EPOLLIN | EPOLLRDNORM; @@ -712,6 +749,7 @@ static __poll_t hid_sensor_custom_poll(struct file *file, } static const struct file_operations hid_sensor_custom_fops = { + .owner = THIS_MODULE, .open = hid_sensor_custom_open, .read = hid_sensor_custom_read, .release = hid_sensor_custom_release, @@ -734,20 +772,17 @@ static int hid_sensor_custom_dev_if_add(struct hid_sensor_custom *sensor_inst) sensor_inst->custom_dev.name = dev_name(&sensor_inst->pdev->dev); sensor_inst->custom_dev.fops = &hid_sensor_custom_fops; ret = misc_register(&sensor_inst->custom_dev); - if (ret) { - kfifo_free(&sensor_inst->data_fifo); - return ret; - } - return 0; + return ret; } static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom *sensor_inst) { - wake_up(&sensor_inst->wait); + mutex_lock(&sensor_inst->mutex); + WRITE_ONCE(sensor_inst->removed, true); + mutex_unlock(&sensor_inst->mutex); + wake_up_interruptible_all(&sensor_inst->wait); misc_deregister(&sensor_inst->custom_dev); - kfifo_free(&sensor_inst->data_fifo); - } /* @@ -970,8 +1005,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) int ret; const struct hid_sensor_custom_match *match; - sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst), - GFP_KERNEL); + sensor_inst = kzalloc_obj(*sensor_inst); if (!sensor_inst) return -ENOMEM; @@ -981,6 +1015,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) sensor_inst->hsdev = hsdev; sensor_inst->pdev = pdev; mutex_init(&sensor_inst->mutex); + kref_init(&sensor_inst->ref); platform_set_drvdata(pdev, sensor_inst); ret = hid_sensor_custom_get_known(hsdev, &match); @@ -992,7 +1027,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "register_platform_device failed\n"); - return ret; + goto err_free; } return 0; @@ -1002,7 +1037,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) &sensor_inst->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - return ret; + goto err_free; } ret = sysfs_create_group(&sensor_inst->pdev->dev.kobj, @@ -1027,6 +1062,8 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) &enable_sensor_attr_group); err_remove_callback: sensor_hub_remove_callback(hsdev, hsdev->usage); +err_free: + kref_put(&sensor_inst->ref, hid_sensor_custom_free); return ret; } @@ -1038,14 +1075,18 @@ static void hid_sensor_custom_remove(struct platform_device *pdev) if (sensor_inst->custom_pdev) { platform_device_unregister(sensor_inst->custom_pdev); - return; + goto out; } hid_sensor_custom_dev_if_remove(sensor_inst); hid_sensor_custom_remove_attributes(sensor_inst); sysfs_remove_group(&sensor_inst->pdev->dev.kobj, &enable_sensor_attr_group); + if (sensor_inst->enable) + sensor_hub_device_close(hsdev); sensor_hub_remove_callback(hsdev, hsdev->usage); +out: + kref_put(&sensor_inst->ref, hid_sensor_custom_free); } static const struct platform_device_id hid_sensor_custom_ids[] = { diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 90666ff629de..2bac8b82ce37 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -173,7 +173,8 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); unsigned long flags; - spin_lock_irqsave(&pdata->dyn_callback_lock, flags); + spin_lock_irqsave(&pdata->lock, flags); + spin_lock(&pdata->dyn_callback_lock); list_for_each_entry(callback, &pdata->dyn_callback_list, list) if (callback->usage_id == usage_id && callback->hsdev == hsdev) { @@ -181,7 +182,8 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, kfree(callback); break; } - spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); + spin_unlock(&pdata->dyn_callback_lock); + spin_unlock_irqrestore(&pdata->lock, flags); return 0; } ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: Fwd: [BUG] HID: sensor: custom: use-after-free of sensor_inst via open misc device after remove (hard lockup) 2026-10-23 14:12 ` Srinivas Pandruvada @ 2026-10-27 12:36 ` Семён Марьясин 0 siblings, 0 replies; 3+ messages in thread From: Семён Марьясин @ 2026-10-27 12:36 UTC (permalink / raw) To: Srinivas Pandruvada Cc: Jiri Kosina, Jonathan Cameron, Benjamin Tissoires, Haoxiang Li, linux-input, linux-iio [-- Attachment #1: Type: text/plain, Size: 7078 bytes --] Hello! Tested this patch (had to adapt it to apply it to 7.2.8 kernel; attaching the adapted version). Without patch: xxd running while intel_ishtp_hid module is removed -> xxd hangs, system either crashes or at least logs "hid-sensor-hub 001F:8087:0AC2.0004: timeout waiting for response from ISHTP device" With patch: xxd correctly closes with message "No such device", no error in kernel logs, no crashes — checked for several times. пт, 23 окт. 2026 г. в 17:12, Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>: > > Hi, > On Fri, 2026-10-23 at 13:42 +0300, Семён Марьясин wrote: > > Hello, > > > > hid-sensor-custom frees its per-sensor state (sensor_inst, > > devm-allocated) on remove while userspace > > can still hold the misc device /dev/HID-SENSOR-2000e1.N.auto open. A > > reader blocked in read() stays > > queued on sensor_inst->wait in freed memory; when it wakes up, > > finish_wait() spins on a garbage > > wait-queue lock with interrupts disabled and the machine hard-locks. > > Any read()/poll()/release() on such > > an fd after remove is a use-after-free as well. > > > > Please give a try with the attached diff. > > > Thanks, > Srinivas > > > > The Sashiko AI review already flagged this as a pre-existing issue > > on Haoxiang Li's "Fix fields lifetime issues" series > > ("The /dev/HID-SENSOR-* character device operations do not > > synchronize > > with device > > removal, > > leading to Use-After-Free on devm-managed structures"): > > https://lore.kernel.org/linux-input/20260707071545.3087073-1-haoxiang_li2024@163.com/ > > The applied patches don't touch it, and the current mainline is > > unchanged. Below is a real-world hit. > > > > Kernel: 7.2.8 (NixOS; drivers/hid/hid-sensor-custom.c is identical to > > current mainline, > > including ad8fb82b0442 and 3789d0802ddb) > > Hardware: Dell Latitude 5420 (Tiger Lake, Intel ISH), BIOS 1.49.0; > > Dell's vendor ISH sensors appear as > > HID-SENSOR-2000e1.N.auto (e.g. an ST VL53L1 time-of-flight > > sensor). > > Taint: O, from unrelated out-of-tree WWAN modules (qcserial, > > usb_wwan). > > > > How it was hit > > -------------- > > > > 1. echo 1 > /sys/bus/platform/devices/HID-SENSOR- > > 2000e1.2.auto/enable_sensor > > 2. xxd /dev/HID-SENSOR-2000e1.2.auto # streaming reader, left > > running > > 3. modprobe -r intel_ishtp_hid intel_ish_ipc && modprobe > > intel_ish_ipc > > > > The hub went away and was re-enumerated fine; ~30 s later CPU0 > > hard-locked in the still-running xxd > > and the hard-lockup detector panicked the machine (log recovered from > > EFI pstore). > > > > Presumably a minimal reproducer is just a blocked reader plus > > unbinding the platform device > > (echo HID-SENSOR-2000e1.N.auto > > > /sys/bus/platform/drivers/hid_sensor_custom/unbind), > > but only the module-reload path above was actually tested so far. > > > > Log (trimmed) > > ------------- > > > > [146608.877730] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting > > for response from ISHTP device > > [146611.885959] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting > > for response from ISHTP device > > [146614.893909] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting > > for response from ISHTP device > > [146617.901872] hid-sensor-hub 001F:8087:0AC2.0003: timeout waiting > > for response from ISHTP device > > [146619.972550] ish-hid {33AECD58-B679-4E54-9BD9-A04D34F0C226}: > > [hid-ish]: enum_devices_done OK, num_hid_devices=3 > > [146620.031441] hid-sensor-hub 001F:8087:0AC2.0007: hidraw3: SENSOR > > HUB HID v2.00 Device [hid-ishtp 8087:0AC2] on > > [146643.503266] iwlwifi 0000:00:14.3: Queue 3 is stuck 46 70 > > [146648.924391] watchdog: CPU0: Watchdog detected hard LOCKUP on cpu > > 0 > > [146648.924796] CPU: 0 UID: 0 PID: 1316004 Comm: xxd Tainted: G > > O 7.2.8 #1-NixOS PREEMPT(lazy) > > [146648.924804] Hardware name: Dell Inc. Latitude 5420/03P5X2, BIOS > > 1.49.0 09/01/2025 > > [146648.924808] RIP: 0010:native_queued_spin_lock_slowpath+0x64/0x2c0 > > [146648.924851] Call Trace: > > [146648.924855] <TASK> > > [146648.924857] _raw_spin_lock_irqsave+0x3d/0x50 > > [146648.924872] finish_wait+0x3b/0x90 > > [146648.924886] hid_sensor_custom_read+0x102/0x150 > > [hid_sensor_custom] > > [146648.924901] vfs_read+0xbf/0x390 > > [146648.924934] ksys_read+0xbf/0xf0 > > [146648.924939] do_syscall_64+0x134/0x6b0 > > [146648.925010] entry_SYSCALL_64_after_hwframe+0x77/0x7f > > [146648.925083] </TASK> > > [146648.925086] Kernel panic - not syncing: Hard LOCKUP > > > > Also attaching a full dmesg log (with only wi-fi noise filtered out). > > > > Analysis (line numbers as in mainline 73bef5) > > --------------------------------------------- > > > > - hid_sensor_custom_probe() allocates sensor_inst with devm_kzalloc() > > (line 980), so it is freed right > > after hid_sensor_custom_remove() returns, regardless of open files. > > - The misc device's fops reach sensor_inst through > > container_of(file->private_data, ..., custom_dev); > > misc_deregister() does not revoke already-open files, so > > read/poll/release keep dereferencing it. > > - hid_sensor_custom_dev_if_remove() (lines 751-758) does wake_up(), > > misc_deregister(), kfifo_free(). > > The woken reader in hid_sensor_custom_read() (line 664) re- > > evaluates > > wait_event_interruptible(sensor_inst->wait, > > !kfifo_is_empty(&sensor_inst->data_fifo)), finds the fifo > > empty and goes back to sleep on sensor_inst->wait. Nothing tells it > > the device is gone. > > - Once sensor_inst is freed and reused, the next wakeup of that > > reader > > runs finish_wait() on the freed > > wait_queue_head: spin_lock_irqsave() on garbage, hard lockup. > > hid_sensor_custom_release() similarly > > does clear_bit() on freed sensor_inst->misc_opened when the fd is > > finally closed. > > > > Possible fix direction > > ---------------------- > > > > Tie sensor_inst's lifetime to its users rather than to devres, the > > way > > hidraw handles disconnect: > > refcount it (kref: one reference for the device, one per open file; > > kfifo_free() and kfree() on the last > > put), add a "removed" flag set in remove() before > > wake_up_interruptible_all(), include it in the > > wait_event condition, and make read()/poll() return -ENODEV / > > EPOLLHUP > > > EPOLLERR once removed. > > > > The same review pointed out two more problems in this teardown path > > that a fix would naturally cover: > > remove() frees data_fifo before sensor_hub_remove_callback(), so a > > sample arriving in between is > > kfifo_in()'d into freed memory; and an enabled sensor is never > > sensor_hub_device_close()d on remove. > > > > I can test patches on this machine. > > > > Thanks, > > Semyon Maryasin > > > > P.S.: sorry for a possible duplicate, I think I messed with the > > headers. -- С уважением, Семён Марьясин [-- Attachment #2: test_patch.diff --] [-- Type: text/x-patch, Size: 7671 bytes --] diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c index c2b425a..fe67e4b 100644 --- a/drivers/hid/hid-sensor-custom.c +++ b/drivers/hid/hid-sensor-custom.c @@ -11,6 +11,7 @@ #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/kfifo.h> +#include <linux/kref.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/poll.h> @@ -37,6 +38,8 @@ struct hid_sensor_custom_field { struct hid_sensor_custom { struct mutex mutex; + struct kref ref; + bool removed; struct platform_device *pdev; struct hid_sensor_hub_device *hsdev; struct hid_sensor_hub_callbacks callbacks; @@ -643,6 +646,15 @@ static void hid_sensor_custom_remove_attributes(struct hid_sensor_custom * kfree(sensor_inst->fields); } +static void hid_sensor_custom_free(struct kref *ref) +{ + struct hid_sensor_custom *sensor_inst = + container_of(ref, struct hid_sensor_custom, ref); + + kfifo_free(&sensor_inst->data_fifo); + kfree(sensor_inst); +} + static ssize_t hid_sensor_custom_read(struct file *file, char __user *buf, size_t count, loff_t *f_ps) { @@ -656,22 +668,35 @@ static ssize_t hid_sensor_custom_read(struct file *file, char __user *buf, if (count < sizeof(struct hid_sensor_sample)) return -EINVAL; - do { - if (kfifo_is_empty(&sensor_inst->data_fifo)) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + for (;;) { + ret = mutex_lock_interruptible(&sensor_inst->mutex); + if (ret) + return ret; - ret = wait_event_interruptible(sensor_inst->wait, - !kfifo_is_empty(&sensor_inst->data_fifo)); - if (ret) - return ret; + if (sensor_inst->removed) { + ret = -ENODEV; + goto unlock; } ret = kfifo_to_user(&sensor_inst->data_fifo, buf, count, &copied); + if (ret || copied) + goto unlock; + mutex_unlock(&sensor_inst->mutex); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(sensor_inst->wait, + READ_ONCE(sensor_inst->removed) || + !kfifo_is_empty(&sensor_inst->data_fifo)); if (ret) return ret; + } - } while (copied == 0); +unlock: + mutex_unlock(&sensor_inst->mutex); + if (ret) + return ret; return copied; } @@ -684,6 +709,7 @@ static int hid_sensor_custom_release(struct inode *inode, struct file *file) struct hid_sensor_custom, custom_dev); clear_bit(0, &sensor_inst->misc_opened); + kref_put(&sensor_inst->ref, hid_sensor_custom_free); return 0; } @@ -691,14 +717,22 @@ static int hid_sensor_custom_release(struct inode *inode, struct file *file) static int hid_sensor_custom_open(struct inode *inode, struct file *file) { struct hid_sensor_custom *sensor_inst; + int ret; sensor_inst = container_of(file->private_data, struct hid_sensor_custom, custom_dev); - /* We essentially have single reader and writer */ - if (test_and_set_bit(0, &sensor_inst->misc_opened)) - return -EBUSY; + mutex_lock(&sensor_inst->mutex); + if (sensor_inst->removed) { + ret = -ENODEV; + } else if (test_and_set_bit(0, &sensor_inst->misc_opened)) { + ret = -EBUSY; + } else { + kref_get(&sensor_inst->ref); + ret = stream_open(inode, file); + } + mutex_unlock(&sensor_inst->mutex); - return stream_open(inode, file); + return ret; } static __poll_t hid_sensor_custom_poll(struct file *file, @@ -712,6 +746,9 @@ static __poll_t hid_sensor_custom_poll(struct file *file, poll_wait(file, &sensor_inst->wait, wait); + if (READ_ONCE(sensor_inst->removed)) + return EPOLLHUP | EPOLLERR; + if (!kfifo_is_empty(&sensor_inst->data_fifo)) mask = EPOLLIN | EPOLLRDNORM; @@ -719,6 +756,7 @@ static __poll_t hid_sensor_custom_poll(struct file *file, } static const struct file_operations hid_sensor_custom_fops = { + .owner = THIS_MODULE, .open = hid_sensor_custom_open, .read = hid_sensor_custom_read, .release = hid_sensor_custom_release, @@ -741,20 +779,17 @@ static int hid_sensor_custom_dev_if_add(struct hid_sensor_custom *sensor_inst) sensor_inst->custom_dev.name = dev_name(&sensor_inst->pdev->dev); sensor_inst->custom_dev.fops = &hid_sensor_custom_fops; ret = misc_register(&sensor_inst->custom_dev); - if (ret) { - kfifo_free(&sensor_inst->data_fifo); - return ret; - } - return 0; + return ret; } static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom *sensor_inst) { - wake_up(&sensor_inst->wait); + mutex_lock(&sensor_inst->mutex); + WRITE_ONCE(sensor_inst->removed, true); + mutex_unlock(&sensor_inst->mutex); + wake_up_interruptible_all(&sensor_inst->wait); misc_deregister(&sensor_inst->custom_dev); - kfifo_free(&sensor_inst->data_fifo); - } /* @@ -977,8 +1012,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) int ret; const struct hid_sensor_custom_match *match; - sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst), - GFP_KERNEL); + sensor_inst = kzalloc_obj(*sensor_inst); if (!sensor_inst) return -ENOMEM; @@ -988,6 +1022,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) sensor_inst->hsdev = hsdev; sensor_inst->pdev = pdev; mutex_init(&sensor_inst->mutex); + kref_init(&sensor_inst->ref); platform_set_drvdata(pdev, sensor_inst); ret = hid_sensor_custom_get_known(hsdev, &match); @@ -999,7 +1034,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "register_platform_device failed\n"); - return ret; + goto err_free; } return 0; @@ -1009,7 +1044,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) &sensor_inst->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - return ret; + goto err_free; } ret = hid_sensor_custom_add_attributes(sensor_inst); @@ -1034,6 +1069,8 @@ err_remove_attributes: hid_sensor_custom_remove_attributes(sensor_inst); err_remove_callback: sensor_hub_remove_callback(hsdev, hsdev->usage); +err_free: + kref_put(&sensor_inst->ref, hid_sensor_custom_free); return ret; } @@ -1045,7 +1082,7 @@ static void hid_sensor_custom_remove(struct platform_device *pdev) if (sensor_inst->custom_pdev) { platform_device_unregister(sensor_inst->custom_pdev); - return; + goto out; } hid_sensor_custom_dev_if_remove(sensor_inst); @@ -1053,7 +1090,11 @@ static void hid_sensor_custom_remove(struct platform_device *pdev) sysfs_remove_group(&sensor_inst->pdev->dev.kobj, &enable_sensor_attr_group); hid_sensor_custom_remove_attributes(sensor_inst); + if (sensor_inst->enable) + sensor_hub_device_close(hsdev); sensor_hub_remove_callback(hsdev, hsdev->usage); +out: + kref_put(&sensor_inst->ref, hid_sensor_custom_free); } static const struct platform_device_id hid_sensor_custom_ids[] = { diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 6470a29..f270b59 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -173,7 +173,8 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); unsigned long flags; - spin_lock_irqsave(&pdata->dyn_callback_lock, flags); + spin_lock_irqsave(&pdata->lock, flags); + spin_lock(&pdata->dyn_callback_lock); list_for_each_entry(callback, &pdata->dyn_callback_list, list) if (callback->usage_id == usage_id && callback->hsdev == hsdev) { @@ -181,7 +182,8 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, kfree(callback); break; } - spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); + spin_unlock(&pdata->dyn_callback_lock); + spin_unlock_irqrestore(&pdata->lock, flags); return 0; } ^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-27 12:36 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <CAM2dg7eZJEii+fJT1TG8XME-TJFqDtURCsr5XS71E5-5-mUV0A@mail.gmail.com>
2026-10-23 10:42 ` Fwd: [BUG] HID: sensor: custom: use-after-free of sensor_inst via open misc device after remove (hard lockup) Семён Марьясин
2026-10-23 14:12 ` Srinivas Pandruvada
2026-10-27 12:36 ` Семён Марьясин
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox