diff --git a/Documentation/hid/hidintro.rst b/Documentation/hid/hidintro.rst index 73523e315ebd..5d367dfca0b8 100644 --- a/Documentation/hid/hidintro.rst +++ b/Documentation/hid/hidintro.rst @@ -522,3 +522,53 @@ This should really be your last resort. vendor: 0x093a product: 0x2510 ... + +Input Device Registration and Lifecycle +======================================== + +HID drivers that rely on the HID core to register input devices (by using the +``HID_CONNECT_HIDINPUT`` flag, which is part of ``HID_CONNECT_DEFAULT``) +must be aware of the registration timing. + +When ``hid_hw_start(hdev, flags)`` is called with ``HID_CONNECT_HIDINPUT``, +the HID core immediately parses the report descriptor, allocates ``input_dev`` +structures, and calls ``input_register_device()`` for each of them. + +This means the input device becomes **live and visible to userspace** before +``hid_hw_start()`` returns. + +If a driver needs to perform additional configuration on the input device (such +as adding force-feedback support, setting extra bits in ``evbit``, or +assigning custom event handlers), doing so in the ``probe`` function after +``hid_hw_start()`` is **incorrect and racy**. Userspace may trigger +callbacks (like ``play_effect``) via ioctls immediately after registration, +leading to potential NULL pointer dereferences if the driver hasn't finished +initializing its private data. + +The correct way to augment an input device before it is registered is to use the +``.input_configured`` callback in ``struct hid_driver``. This hook is +called by the HID core after the ``input_dev`` is fully formed but **before** +``input_register_device()`` is invoked. + +Example: + +.. code-block:: c + + static int my_input_configured(struct hid_device *hdev, struct hid_input *hidinput) + { + struct input_dev *input = hidinput->input; + + /* Initialize private data and capabilities here */ + set_bit(EV_FF, input->evbit); + return input_ff_create_memless(input, NULL, my_play_effect); + } + + static struct hid_driver my_driver = { + .name = "my_driver", + .probe = my_probe, + .input_configured = my_input_configured, + }; + +Drivers that require even more control over the lifecycle should mask out +``HID_CONNECT_HIDINPUT`` and call ``input_register_device()`` manually +when they are ready. diff --git a/scripts/coccinelle/hid/ff_race.cocci b/scripts/coccinelle/hid/ff_race.cocci new file mode 100644 index 000000000000..479f5d1e3184 --- /dev/null +++ b/scripts/coccinelle/hid/ff_race.cocci @@ -0,0 +1,34 @@ +/// Detect HID drivers that initialize force-feedback after hid_hw_start() +/// when HID_CONNECT_HIDINPUT is used. This is a lifecycle violation as +/// the input device is already registered. +// +// Confidence: High +// Copyright: (C) 2026 Gemini. GPLv2. + +virtual report + +@r@ +identifier probe_fn; +expression hdev, flags; +position p1, p2; +@@ + +probe_fn(struct hid_device *hdev, ...) { + <... + hid_hw_start@p1(hdev, flags) + ... + \(input_ff_create\|input_ff_create_memless\)@p2(...) + ...> +} + +@script:python depends on report@ +p1 << r.p1; +p2 << r.p2; +flags << r.flags; +@@ + +# Check if flags include HID_CONNECT_HIDINPUT (0x01) or HID_CONNECT_DEFAULT (0x0f) +# Note: HID_CONNECT_DEFAULT is 0x0f, HID_CONNECT_HIDINPUT is 0x01 +if "HID_CONNECT_HIDINPUT" in flags or "HID_CONNECT_DEFAULT" in flags: + msg = "WARNING: force-feedback initialized after hid_hw_start() with HID_CONNECT_HIDINPUT. Input device is already registered at this point. Use .input_configured() instead." + coccilib.report.print_report(p2[0], msg)