* Oops when rebinding f_hid while /dev/hidg* is still opened
@ 2026-03-22 14:01 Michael Zimmermann
2026-03-23 11:00 ` Greg Kroah-Hartman
0 siblings, 1 reply; 6+ messages in thread
From: Michael Zimmermann @ 2026-03-22 14:01 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-usb
The steps are as follows:
- setup an hid gadget via configfs
- bind it via the UDC file
- open /dev/hidg0
- unbind it via the UDC file
- bind it via the UDC file
- close /dev/hidg0
This results in an oops (on a Raspberry Pi 4 running Fedora 43 with
kernel 6.17.1-300.fc43.aarch64):
+ echo fe980000.usb
[ 273.407057] dwc2 fe980000.usb: bound driver configfs-gadget.g1
+ sleep 3
[ 273.624208] dwc2 fe980000.usb: new device is high-speed
[ 273.748314] dwc2 fe980000.usb: new device is high-speed
[ 273.811272] dwc2 fe980000.usb: new address 30
+ exec
+ echo ''
+ echo fe980000.usb
[ 276.423944] dwc2 fe980000.usb: bound driver configfs-gadget.g1
+ exec
[ 276.432878] slab kmalloc-rnd-05-2k start ffff0000023c8000 pointer
offset 1184 size 2048
[ 276.440740] list_del corruption. prev->next should be
ffff00000c72e7f0, but was ffff0000023c84a0. (prev=ffff0000023c84a0)
[ 276.451905] ------------[ cut here ]------------
[ 276.456345] kernel BUG at lib/list_debug.c:62!
[ 276.460836] Internal error: Oops - BUG: 00000000f2000800 [#1] SMP
[ 276.467096] Modules linked in: usb_f_hid libcomposite nft_fib_inet
nft_fib_ipv4 nft_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4
nf_reject_ipv6 nft_reject nft_ct nft_chain_nat nf_nat nf_conntrack
nf_defrag_ipv6 nf_defrag_ipv4 nf_tables qrtr bnep brcmfmac_wcc
bcm2835_v4l2(C) bcm2835_mmal_vchiq(C) videobuf2_vmalloc
videobuf2_memops videobuf2_v4l2 videodev btsdio videobuf2_common
cpufreq_dt mc snd_bcm2835(C) vc4 snd_soc_hdmi_codec brcmfmac hci_uart
btqca drm_exec btrtl snd_soc_core btintel btbcm brcmutil bluetooth
cfg80211 snd_compress ac97_bus snd_pcm_dmaengine snd_seq
snd_seq_device pwrseq_core raspberrypi_cpufreq snd_pcm rfkill
pwm_bcm2835 snd_timer iproc_rng200 snd v3d soundcore
drm_display_helper vchiq(C) bcm2711_thermal gpu_sched cec
i2c_mux_pinctrl ledtrig_default_on leds_gpio i2c_mux drm_dma_helper
binfmt_misc vfat fat loop dm_multipath zram lz4hc_compress
lz4_compress xfs mmc_block rpmb_core reset_gpio dwc2
gpio_raspberrypi_exp raspberrypi_hwmon sdhci_iproc broadcom
pwrseq_simple sdhci_pltfm bcm_phy_ptp
[ 276.467432] bcm_phy_lib udc_core i2c_brcmstb sdhci clk_bcm2711_dvp
mmc_core genet i2c_bcm2835 mdio_bcm_unimac bcm2835_wdt bcm2835_dma
phy_generic nvmem_rmem sunrpc be2iscsi bnx2i cnic uio cxgb4i cxgb4 tls
cxgb3i cxgb3 mdio libcxgbi libcxgb qla4xxx iscsi_boot_sysfs iscsi_tcp
libiscsi_tcp libiscsi scsi_transport_iscsi scsi_dh_rdac scsi_dh_emc
scsi_dh_alua i2c_dev fuse nfnetlink aes_neon_bs
[ 276.592961] CPU: 0 UID: 0 PID: 1756 Comm: bash Tainted: G C
6.17.1-300.fc43.aarch64 #1 PREEMPT(voluntary)
[ 276.604226] Tainted: [C]=CRAP
[ 276.607215] Hardware name: raspberrypi Raspberry Pi 4 Model B Rev
1.1/Raspberry Pi 4 Model B Rev 1.1, BIOS 2025.10 10/01/2025
[ 276.618677] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 276.625725] pc : __list_del_entry_valid_or_report+0xb8/0x110
[ 276.631451] lr : __list_del_entry_valid_or_report+0xb8/0x110
[ 276.637179] sp : ffff80008082bb80
[ 276.640527] x29: ffff80008082bb80 x28: ffff00000509a540 x27: 0000000000000000
[ 276.647756] x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000
[ 276.654982] x23: 0000000000000001 x22: ffffbc93e4208d00 x21: ffff00000c72e7f0
[ 276.662209] x20: ffffbc93e69cbf90 x19: ffff00000c72e5b8 x18: 00000000ffffffff
[ 276.669435] x17: 20747562202c3066 x16: 3765323763303030 x15: 0720072007200720
[ 276.676661] x14: 0720072007200720 x13: 0720072007200720 x12: 0720072007200720
[ 276.683888] x11: 0000000000000001 x10: 0000000000000001 x9 : ffffbc93e2ef94c8
[ 276.691114] x8 : ffffbc93e63b8168 x7 : ffff80008082b8f0 x6 : 0000000000000001
[ 276.698341] x5 : 0000000000000000 x4 : 0000000000000001 x3 : 0000000000000000
[ 276.705568] x2 : 0000000000000000 x1 : ffff00000509a540 x0 : 000000000000006d
[ 276.712794] Call trace:
[ 276.715262] __list_del_entry_valid_or_report+0xb8/0x110 (P)
[ 276.720990] cd_forget+0x3c/0x90
[ 276.724250] evict+0x220/0x250
[ 276.727333] iput_final+0xb8/0x160
[ 276.730771] iput.part.0+0x104/0x130
[ 276.734384] iput+0x24/0x40
[ 276.737203] dentry_unlink_inode+0xc8/0x1a0
[ 276.741435] __dentry_kill+0x84/0x200
[ 276.745135] dput+0x80/0xe8
[ 276.747956] __fput+0x12c/0x300
[ 276.751128] fput_close_sync+0x40/0x120
[ 276.755006] __arm64_sys_close+0x40/0x98
[ 276.758972] invoke_syscall.constprop.0+0x64/0xe8
[ 276.763731] el0_svc_common.constprop.0+0x40/0xe8
[ 276.768490] do_el0_svc+0x24/0x38
[ 276.771839] el0_svc+0x3c/0x168
[ 276.775010] el0t_64_sync_handler+0xa0/0xf0
[ 276.779242] el0t_64_sync+0x1b0/0x1b8
[ 276.782947] Code: a94107e3 9129a000 f9400062 97d7bf71 (d4210000)
[ 276.789118] ---[ end trace 0000000000000000 ]---
[ 276.793784] note: bash[1756] exited with irqs disabled
[ 276.799026] dwc2 fe980000.usb: new device is high-speed
Segmentation fault bash -x trigger_udcbug.sh
I think the issue happens while calling
`list_del_init(&inode->i_devices);` in cd_forget.
I did some research and a similar bug was fixed in:
89ff3dfac604 usb: gadget: f_hid: fix f_hidg lifetime vs cdev
I assume that the difference is, that they only fixed an issue which
occurred while unbinding, but never tried to rebind it afterwards.
I'm not familiar with the code, but could the issue be, that f_hid
calls cdev_init during bind, which memsets the entire cdev structure?
Here's the script I use to trigger this bug:
export CONFIGFS_HOME=/sys/kernel/config
export DEVNAME="fe980000.usb"
mkdir -p $CONFIGFS_HOME/usb_gadget/g1
cd $CONFIGFS_HOME/usb_gadget/g1
mkdir configs/c.1
mkdir -p functions/hid.usb0
echo 0 > functions/hid.usb0/protocol
echo 64 > functions/hid.usb0/report_length
ln -s functions/hid.usb0 configs/c.1
echo "$DEVNAME" > UDC
sleep 3
exec 3<> /dev/hidg0
echo "" > UDC
echo "$DEVNAME" > UDC
exec 3<&-
Thanks
Michael
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Oops when rebinding f_hid while /dev/hidg* is still opened
2026-03-22 14:01 Oops when rebinding f_hid while /dev/hidg* is still opened Michael Zimmermann
@ 2026-03-23 11:00 ` Greg Kroah-Hartman
2026-03-23 17:11 ` Michael Zimmermann
0 siblings, 1 reply; 6+ messages in thread
From: Greg Kroah-Hartman @ 2026-03-23 11:00 UTC (permalink / raw)
To: Michael Zimmermann; +Cc: linux-usb
On Sun, Mar 22, 2026 at 03:01:19PM +0100, Michael Zimmermann wrote:
> The steps are as follows:
> - setup an hid gadget via configfs
> - bind it via the UDC file
> - open /dev/hidg0
> - unbind it via the UDC file
> - bind it via the UDC file
> - close /dev/hidg0
>
> This results in an oops (on a Raspberry Pi 4 running Fedora 43 with
> kernel 6.17.1-300.fc43.aarch64):
> + echo fe980000.usb
> [ 273.407057] dwc2 fe980000.usb: bound driver configfs-gadget.g1
> + sleep 3
> [ 273.624208] dwc2 fe980000.usb: new device is high-speed
> [ 273.748314] dwc2 fe980000.usb: new device is high-speed
> [ 273.811272] dwc2 fe980000.usb: new address 30
This bug has happened a lot on many gadget drivers, I think we fixed a
bunch of them already, can you verify this is still an issue on the
latest 7.0-rc4 or 6.19.y kernel release (6.17 is very old and obsolete
and insecure, don't run that thing...)
thanks,
greg k-h
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Oops when rebinding f_hid while /dev/hidg* is still opened
2026-03-23 11:00 ` Greg Kroah-Hartman
@ 2026-03-23 17:11 ` Michael Zimmermann
2026-03-24 15:28 ` Greg Kroah-Hartman
0 siblings, 1 reply; 6+ messages in thread
From: Michael Zimmermann @ 2026-03-23 17:11 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-usb
> This bug has happened a lot on many gadget drivers, I think we fixed a
> bunch of them already, can you verify this is still an issue on the
> latest 7.0-rc4 or 6.19.y kernel release (6.17 is very old and obsolete
> and insecure, don't run that thing...)
Apparently, Fedora updates their images very rarely and they need a
package manager update, sorry about that.
I now tested it 6.19.8-200.fc43.aarch64 and the bug is still there. I
then switched to testing with buildroot, because that makes it easier
to test different kernel versions. There, the behavior is a bit
different but there's still a bug: Instead of immediately triggering
an oops, everything appears fine on the first run of my script. But on
the second run I see this:
[ 81.514126] refcount_t: underflow; use-after-free.
[ 81.519000] WARNING: lib/refcount.c:28 at
refcount_warn_saturate+0xf4/0x150, CPU#2: bash/176
[ 81.527501] Modules linked in: usb_f_hid libcomposite
[ 81.532613] CPU: 2 UID: 0 PID: 176 Comm: bash Not tainted 7.0-rc5 #2 PREEMPT
[ 81.539749] Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT)
[ 81.545654] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 81.552704] pc : refcount_warn_saturate+0xf4/0x150
[ 81.557550] lr : refcount_warn_saturate+0xf4/0x150
[ 81.562398] sp : ffff800080613a90
[ 81.565746] x29: ffff800080613a90 x28: ffff0000048d0f40 x27: 0000000000000000
[ 81.572973] x26: 0000000000000000 x25: 0000000000000000 x24: ffff000003e22000
[ 81.580199] x23: ffff000003f39a80 x22: ffff000003e22618 x21: ffff000003f39968
[ 81.587425] x20: ffff000003e22c58 x19: ffff000003e22c58 x18: ffff80008061367f
[ 81.594653] x17: 0000000000000000 x16: ffffba3cf8d80650 x15: 072007200720072e
[ 81.601879] x14: 0765076507720766 x13: 072007200720072e x12: ffffba3cfac96be8
[ 81.609105] x11: 0000000000000058 x10: 0000000000000018 x9 : ffffba3cfac96be8
[ 81.616332] x8 : 0000000000000102 x7 : ffffba3cfaceebe8 x6 : ffffba3cfaceebe8
[ 81.623558] x5 : 0000000000017fe8 x4 : 0000000000000000 x3 : 0000000000000000
[ 81.630785] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff0000048d0f40
[ 81.638012] Call trace:
[ 81.640479] refcount_warn_saturate+0xf4/0x150 (P)
[ 81.645326] kobject_put+0x108/0x110
[ 81.648938] cdev_device_del+0x50/0x60
[ 81.652728] hidg_unbind+0x24/0x50 [usb_f_hid]
[ 81.657222] purge_configs_funcs+0x7c/0xe0 [libcomposite]
[ 81.662686] configfs_composite_unbind+0x5c/0xb0 [libcomposite]
[ 81.668680] gadget_unbind_driver+0x7c/0x120
[ 81.672997] device_remove+0x4c/0x80
[ 81.676610] device_release_driver_internal+0x1cc/0x230
[ 81.681898] driver_detach+0x4c/0xa0
[ 81.685511] bus_remove_driver+0x6c/0xc0
[ 81.689478] driver_unregister+0x30/0x60
[ 81.693443] usb_gadget_unregister_driver+0x20/0x40
[ 81.698379] gadget_dev_desc_UDC_store+0xb4/0x140 [libcomposite]
[ 81.704459] configfs_write_iter+0xc4/0x120
[ 81.708690] vfs_write+0x244/0x370
[ 81.712127] ksys_write+0x70/0x110
[ 81.715564] __arm64_sys_write+0x1c/0x30
[ 81.719529] invoke_syscall+0x48/0x110
[ 81.723319] el0_svc_common.constprop.0+0x40/0xe0
[ 81.728077] do_el0_svc+0x1c/0x30
[ 81.731426] el0_svc+0x34/0x110
[ 81.734599] el0t_64_sync_handler+0xa0/0xf0
[ 81.738829] el0t_64_sync+0x198/0x19c
I get this with both 6.19.8 and 7.0-rc5 on buildroot. I guess there's
some randomness to this bug and the different kernel config changes
the outcome a little bit.
Thanks
Michael
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Oops when rebinding f_hid while /dev/hidg* is still opened
2026-03-23 17:11 ` Michael Zimmermann
@ 2026-03-24 15:28 ` Greg Kroah-Hartman
2026-03-24 19:56 ` Michael Zimmermann
0 siblings, 1 reply; 6+ messages in thread
From: Greg Kroah-Hartman @ 2026-03-24 15:28 UTC (permalink / raw)
To: Michael Zimmermann; +Cc: linux-usb
On Mon, Mar 23, 2026 at 06:11:27PM +0100, Michael Zimmermann wrote:
> > This bug has happened a lot on many gadget drivers, I think we fixed a
> > bunch of them already, can you verify this is still an issue on the
> > latest 7.0-rc4 or 6.19.y kernel release (6.17 is very old and obsolete
> > and insecure, don't run that thing...)
>
> Apparently, Fedora updates their images very rarely and they need a
> package manager update, sorry about that.
>
> I now tested it 6.19.8-200.fc43.aarch64 and the bug is still there. I
> then switched to testing with buildroot, because that makes it easier
> to test different kernel versions. There, the behavior is a bit
> different but there's still a bug: Instead of immediately triggering
> an oops, everything appears fine on the first run of my script. But on
> the second run I see this:
> [ 81.514126] refcount_t: underflow; use-after-free.
Ok, something is not being initialized again properly.
Take a look at the patch series at commit 41f71deda1c1 ("Merge patch
series "usb: gadget: Refactor function drivers to use __free()
cleanup"")
Perhaps something like commit 42988380ac67 ("usb: gadget: f_ecm:
Refactor bind path to use __free()") should be done for the f_hid
driver? Can you work on that as you have a way to test this well, or do
you want a patch to test?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Oops when rebinding f_hid while /dev/hidg* is still opened
2026-03-24 15:28 ` Greg Kroah-Hartman
@ 2026-03-24 19:56 ` Michael Zimmermann
2026-03-27 18:46 ` Michael Zimmermann
0 siblings, 1 reply; 6+ messages in thread
From: Michael Zimmermann @ 2026-03-24 19:56 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-usb
> Ok, something is not being initialized again properly.
>
> Take a look at the patch series at commit 41f71deda1c1 ("Merge patch
> series "usb: gadget: Refactor function drivers to use __free()
> cleanup"")
>
> Perhaps something like commit 42988380ac67 ("usb: gadget: f_ecm:
> Refactor bind path to use __free()") should be done for the f_hid
> driver? Can you work on that as you have a way to test this well, or do
> you want a patch to test?
First off, I had an aha moment about `89ff3dfac604 usb: gadget: f_hid:
fix f_hidg lifetime vs cdev`, which I mentioned in my original mail:
That one seems to have fixed removing an hid function while the cdev
is opened. The issue I'm seeing is about binding/unbinding the udc
while the cdev is opened.
I don't see anything wrong with the goto error handling in f_hid's
unbind, but I've done some tests regarding my suspicion about
cdev_init, and it looks like that is actually the problem. Calling it
on an already initialized structure is simply unsafe (unless there are
no references left). The lifetime of the cdev is bound to the device
in hidg, but the bind/unbind procedure can happen independent of that.
I see two solutions to that:
- Move cdev_init to hidg_alloc and cdev_device_del to hidg_release.
I've tested this and it removes the issue, but it changes the
behavior. With that, the /dev/hidg* will be created as soon as you
allocate the function and bind/unbind of a udc device has no effect on
its existence. Only deleting a function will remove it.
- Use cdev_alloc in hidg_alloc. This would allow replacing the cdev
without invalidating the old one. This would probably require
introducing a global variable with a list of hidg functions though to
provide an alternative to "container_of(inode->i_cdev, struct f_hidg,
cdev);"
Any thoughts about the best way to proceed with this?
Thanks
Michael
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Oops when rebinding f_hid while /dev/hidg* is still opened
2026-03-24 19:56 ` Michael Zimmermann
@ 2026-03-27 18:46 ` Michael Zimmermann
0 siblings, 0 replies; 6+ messages in thread
From: Michael Zimmermann @ 2026-03-27 18:46 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-usb
> - Move cdev_init to hidg_alloc and cdev_device_del to hidg_release.
> I've tested this and it removes the issue, but it changes the
> behavior. With that, the /dev/hidg* will be created as soon as you
> allocate the function and bind/unbind of a udc device has no effect on
> its existence. Only deleting a function will remove it.
That didn't work out, because it leaks the kobj due to recursive references.
> - Use cdev_alloc in hidg_alloc. This would allow replacing the cdev
> without invalidating the old one. This would probably require
> introducing a global variable with a list of hidg functions though to
> provide an alternative to "container_of(inode->i_cdev, struct f_hidg,
> cdev);"
I found a way to implement this without a global list and will send a patch.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-03-27 18:46 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-22 14:01 Oops when rebinding f_hid while /dev/hidg* is still opened Michael Zimmermann
2026-03-23 11:00 ` Greg Kroah-Hartman
2026-03-23 17:11 ` Michael Zimmermann
2026-03-24 15:28 ` Greg Kroah-Hartman
2026-03-24 19:56 ` Michael Zimmermann
2026-03-27 18:46 ` Michael Zimmermann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox