Re: [PATCH v2 7/7] usb: gadget: f_ncm: Fix net_device lifecycle with device_move
From: Kuen-Han Tsai
Date: Mon Mar 16 2026 - 02:03:50 EST
Hi Val,
On Sun, Mar 15, 2026 at 1:21 PM Val Packett <val@xxxxxxxxxxxx> wrote:
>
> Hi,
>
> On 3/9/26 9:04 AM, Kuen-Han Tsai wrote:
> > The network device outlived its parent gadget device during
> > disconnection, resulting in dangling sysfs links and null pointer
> > dereference problems.
> >
> > A prior attempt to solve this by removing SET_NETDEV_DEV entirely [1]
> > was reverted due to power management ordering concerns and a NO-CARRIER
> > regression.
> >
> > A subsequent attempt to defer net_device allocation to bind [2] broke
> > 1:1 mapping between function instance and network device, making it
> > impossible for configfs to report the resolved interface name. This
> > results in a regression where the DHCP server fails on pmOS.
> >
> > [..]
>
> I just saw that this was the last commit touching u_ether while
> debugging… the DHCP server failing on pmOS. (In the initrd, even).
>
> Specifically, udev calling ethtool_get_drvinfo and eth_get_drvinfo
> dereferencing an unset dev->gadget:
Thanks for the report and testing.
I can reproduce the problem on a Pixel 3 by dropping into the pmOS
debug shell. When pmOS drops into the debug shell, it temporarily
unbinds the gadget to reconfigure the USB functions. Since my recent
patch intentionally reparented the net_device to /sys/devices/virtual
during unbind, dev>gadget became NULL. Meanwhile, the ethtool queries
on the surviving interface, leading to a null pointer dereference.
>
> [ 7.528277] [pmOS-rd]: Setting up USB gadget through configfs
> [ 7.539437] configfs-gadget.g1 gadget.0: HOST MAC 2a:a6:63:b7:92:23
> [ 7.545914] configfs-gadget.g1 gadget.0: MAC 76:1d:2b:16:aa:25
> [ 7.577888] [pmOS-rd]: Trying to start server with parameters: Server
> IP addr: 172.16.42.1:67, client IP addr: 172.16.42.2, interface: usb0
> [ 7.591522] [pmOS-rd]: Entering debug shell
> [ 7.597590] Unable to handle kernel NULL pointer dereference at
> virtual address 0000000000000080
> [ 7.606670] Mem abort info:
> [ 7.609571] ESR = 0x0000000096000004
> [ 7.613462] EC = 0x25: DABT (current EL), IL = 32 bits
> [ 7.618942] SET = 0, FnV = 0
> [ 7.622105] EA = 0, S1PTW = 0
> [ 7.625354] FSC = 0x04: level 0 translation fault
> [ 7.630395] Data abort info:
> [ 7.630398] ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000
> [ 7.630401] CM = 0, WnR = 0, TnD = 0, TagAccess = 0
> [ 7.630404] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
> [ 7.630407] user pgtable: 4k pages, 48-bit VAs, pgdp=0000000107b18000
> [ 7.630411] [0000000000000080] pgd=0000000000000000, p4d=0000000000000000
> [ 7.630420] Internal error: Oops: 0000000096000004 [#1] SMP
> [ 7.630425] Modules linked in: typec msm ubwc_config mdt_loader ocmem
> rtc_pm8xxx drm_gpuvm drm_exec i2c_qcom_geni llcc_qcom gpi gpu_sched
> drm_client_lib phy_qcom_snps_femto_v2 drm_display_helper cec
> drm_dp_aux_bus icc_bwmon drm_kms_helper drm backlight ufs_qcom
> phy_qcom_qmp_ufs icc_osm_l3 pmic_glink pdr_interface qcom_pdr_msg
> qmi_helpers
> [ 7.630486] CPU: 1 UID: 0 PID: 175 Comm: (udev-worker) Tainted: G
> W 7.0.0-rc3-next-20260313-00118-gf4f287b6004a-dirty #59 PREEMPT(full)
> [ 7.630493] Tainted: [W]=WARN
> [ 7.630495] Hardware name: Motorola edge 30 (DT)
> [ 7.630499] pstate: 80400005 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS
> BTYPE=--)
> [ 7.630503] pc : eth_get_drvinfo+0x50/0x90 <..snip..>
> [ 7.630595] Call trace:
> [ 7.630598] eth_get_drvinfo+0x50/0x90 (P)
> [ 7.630608] ethtool_get_drvinfo+0x5c/0x1f0
> [ 7.630617] __dev_ethtool+0xaec/0x1fe0
> [ 7.630622] dev_ethtool+0x134/0x2e0
> [ 7.630627] dev_ioctl+0x338/0x560
> [ 7.630633] sock_do_ioctl+0xe0/0x128
> [ 7.630642] sock_ioctl+0x2cc/0x3e0
> [ 7.630647] __arm64_sys_ioctl+0xac/0x108
> [ 7.630656] invoke_syscall.constprop.0+0x48/0x100
> [ 7.630664] el0_svc_common.constprop.0+0x40/0xe8
> [ 7.630670] do_el0_svc+0x24/0x38
> [ 7.630676] el0_svc+0x34/0x180
> [ 7.642931] [pmOS-rd]: /usr/bin/buffyboard
> [ 7.644473] el0t_64_sync_handler+0xa0/0xe8
> [ 7.644482] el0t_64_sync+0x17c/0x180
> [ 7.644491] Code: 91094021 94134bd9 f9457680 d2800402 (f9404001)
> [ 7.644495] ---[ end trace 0000000000000000 ]---
>
> As a "workaround", this works:
>
>
> --- a/drivers/usb/gadget/function/u_ether.c
> +++ b/drivers/usb/gadget/function/u_ether.c
> @@ -113,8 +113,14 @@
>
> strscpy(p->driver, "g_ether", sizeof(p->driver));
> strscpy(p->version, UETH__VERSION, sizeof(p->version));
> - strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
> - strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
> + if (dev->gadget) {
> + strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
> + strscpy(p->bus_info, dev_name(&dev->gadget->dev),
> sizeof(p->bus_info));
> + } else {
> + pr_warn("%s: called with no gadget set\n", __func__);
> + strscpy(p->fw_version, "N/A", sizeof(p->fw_version));
> + strscpy(p->bus_info, "platform", sizeof(p->bus_info));
> + }
> }
>
> /* REVISIT can also support:
>
> ..or would that not be a workaround? The lifecycle of gadget being set
> seems kinda decoupled from the lifecycle of the registration (??) And as
> long as it's registered, the dev info can be queried (?)
>
>
> Thanks,
> ~val
>
I believe your suggested fix correctly handles this detached state.
One minor suggestion: looking at ethtool_get_drvinfo() in
net/ethtool/ioctl.c, we can simply skip the strscpy calls entirely
when dev->gadget is NULL. ethtool_get_drvinfo() checks if bus_info or
fw_version are empty strings and handles the fallback natively, so we
don't need to explicitly copy "N/A" or "platform".
I'll send out a standalone fix shortly and will include the
Suggested-by and Reported-by tags for you. Thanks again for catching
this!
Regards,
Kuen-Han