Re: [PATCH v2] HID: hid-oxp: fix UAF on pending work in remove()

[email protected] Tue, 04 Aug 2026 10:06:18 +0000
Newsgroups org.kernel.vger.linux-input
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 6 potential issue(=
s) to consider:

New issues:
- [High] Global workqueues permanently disabled on inert interface removal

Pre-existing issues:
- [High] Use-After-Free on devres-managed led_classdev in probe error path
- [High] Uninitialized work struct access during early raw events
- [High] State corruption and Use-After-Free due to static global drvdata
- [High] NULL Pointer Dereference in early raw event handling
- [Medium] Out-of-Bounds Read in raw event handler
--

--- Patch [1]: [PATCH v2] HID: hid-oxp: fix UAF on pending work in remove()=
 ---
commit 3701229efb152cb11ba2c2b9567116728fd2754a
Author: Shengzhuo Wei <[email protected]>

HID: hid-oxp: fix UAF on pending work in remove()

The patch updates the hid-oxp driver to use disable_delayed_work_sync()
instead of cancel_delayed_work() in the remove path to prevent workers from
firing after device teardown. It also delays the initialization and arming
of the oxp_mcu_init worker in probe to prevent it from pending during a pro=
be
failure.

Link: https://lore.kernel.org/r/[email protected]

> diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c
> --- a/drivers/hid/hid-oxp.c
> +++ b/drivers/hid/hid-oxp.c
> @@ -1501,14 +1501,14 @@ static int oxp_cfg_probe(struct hid_device *hdev,=
 u16 up)
>  	drvdata.gamepad_mode =3D OXP_GP_MODE_XINPUT;
>  	drvdata.rumble_intensity =3D 5;
> =20
> -	INIT_DELAYED_WORK(&drvdata.oxp_mcu_init, oxp_mcu_init_fn);
> -	mod_delayed_work(system_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50)=
);
> -
>  	ret =3D devm_device_add_group(&hdev->dev, &oxp_cfg_attrs_group);
>  	if (ret)
>  		return dev_err_probe(&hdev->dev, ret,
>  				     "Failed to attach configuration attributes\n");
> =20
> +	INIT_DELAYED_WORK(&drvdata.oxp_mcu_init, oxp_mcu_init_fn);
> +	mod_delayed_work(system_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50)=
);
> +

[Severity: High]
This is a pre-existing issue, but does moving the initialization to the end
of probe widen a race window with raw events?

Since hid_hw_start() and hid_hw_open() are called earlier in oxp_hid_probe(=
),
the hardware is active and raw events can arrive before probe completes. If=
 a
device sends a status report early, oxp_hid_raw_event_gen_2() calls
mod_delayed_work() on drvdata.oxp_mcu_init.

Because the delayed work is now initialized at the very end of oxp_cfg_prob=
e(),
could an early event queue an uninitialized work item with a null function
pointer, leading to a kernel panic when the timer fires?

[Severity: High]
This is a pre-existing issue, but does the probe error path leak the pending
work if we fail earlier?

If oxp_cfg_probe() fails after devm_led_classdev_multicolor_register(), for
example during devm_device_add_group(), it returns without canceling
drvdata.oxp_rgb_queue:

oxp_cfg_probe() {
    ...
    ret =3D devm_device_add_group(drvdata.led_mc->led_cdev.dev,
                                &oxp_rgb_attr_group);
    if (ret)
        return dev_err_probe(...);
}

Since the LED class device is briefly registered, userspace or udev can
trigger oxp_rgb_brightness_set(), queueing the delayed work. When probe
returns an error, devres frees the led_classdev. Could the pending work
execute and dereference the freed device pointer?

[Severity: High]
This is a pre-existing issue, but does the driver support multiple devices
safely?

The driver uses a single static global drvdata struct to store per-device
state:

static struct oxp_hid_cfg drvdata;

In oxp_cfg_probe(), this global state is overwritten for each probed device:

oxp_cfg_probe() {
    hid_set_drvdata(hdev, &drvdata);
    mutex_init(&drvdata.cfg_mutex);
    drvdata.hdev =3D hdev;
    ...
}

If multiple OneXPlayer devices are connected, the second call will overwrite
drvdata.hdev and reinitialize cfg_mutex, destroying the mutex state for the
first device. Could unplugging one device then leave dangling pointers and
corrupted work structures for the remaining device?

[Severity: High]
This is a pre-existing issue, but could early raw events trigger a null
pointer dereference?

Because hid_hw_start() and hid_hw_open() are called before
oxp_cfg_probe(), raw events can be routed to oxp_hid_raw_event_gen_2()
before drvdata.led_mc is initialized.

Since drvdata is zero-initialized, an early event matching
OXP_FID_GEN2_STATUS_EVENT and OXP_GET_PROPERTY would access
drvdata.led_mc:

oxp_hid_raw_event_gen_2() {
    ...
    led_mc->led_cdev.brightness =3D rgb_rep->brightness / 4 *
                                  led_mc->led_cdev.max_brightness;
}

Will this dereference a null pointer if probe hasn't completed?

[Severity: Medium]
This is a pre-existing issue, but does the raw event handler verify the
report size?

The HID core passes the actual report size to the raw event handler, but
oxp_hid_raw_event() formats OXP_PACKET_SIZE bytes:

oxp_hid_raw_event() {
    ...
    dev_dbg(&hdev->dev, "raw event data: [%*ph]\n", OXP_PACKET_SIZE, data);
}

oxp_hid_raw_event_gen_2() also accesses specific offsets like data[3] witho=
ut
checking the boundaries.

If a malicious USB device sends a short HID report, could these handlers
read beyond the allocated buffer and leak uninitialized memory to dmesg?

>  	return 0;
>  }

[ ... ]

> @@ -1552,9 +1552,9 @@ static int oxp_hid_probe(struct hid_device *hdev,
> =20
>  static void oxp_hid_remove(struct hid_device *hdev)
>  {
> -	cancel_delayed_work(&drvdata.oxp_rgb_queue);
> -	cancel_delayed_work(&drvdata.oxp_btn_queue);
> -	cancel_delayed_work(&drvdata.oxp_mcu_init);
> +	disable_delayed_work_sync(&drvdata.oxp_rgb_queue);
> +	disable_delayed_work_sync(&drvdata.oxp_btn_queue);
> +	disable_delayed_work_sync(&drvdata.oxp_mcu_init);

[Severity: High]
Do these calls to disable_delayed_work_sync() permanently break the workers?

Because the driver successfully binds to all interfaces of the USB device
and uses a single global drvdata structure, unbinding any inert interface
will trigger oxp_hid_remove().

Since disable_delayed_work_sync() is used, the works are disabled. However,
enable_delayed_work() is never called anywhere in the driver. Could
removing an inert interface permanently break RGB updates, button mappings,
and MCU initialization for the active gamepad interface until it is rebound?

>  	hid_hw_close(hdev);
>  	hid_hw_stop(hdev);
>  }

--=20
Sashiko AI review =C2=B7 https://sashiko.dev/#/patchset/20260804-oxp-fix-v2=
[email protected]?part=3D1