PT-2026-35019 · Linux · Linux
Published
2026-04-24
·
Updated
2026-04-24
·
CVE-2026-31667
None
No severity ratings or metrics are available. When they are, we'll update the corresponding info on the page.
In the Linux kernel, the following vulnerability has been resolved:
Input: uinput - fix circular locking dependency with ff-core
A lockdep circular locking dependency warning can be triggered
reproducibly when using a force-feedback gamepad with uinput (for
example, playing ELDEN RING under Wine with a Flydigi Vader 5
controller):
ff->mutex -> udev->mutex -> input mutex -> dev->mutex -> ff->mutex
The cycle is caused by four lock acquisition paths:
-
ff upload: input ff upload() holds ff->mutex and calls uinput dev upload effect() -> uinput request submit() -> uinput request send(), which acquires udev->mutex.
-
device create: uinput ioctl handler() holds udev->mutex and calls uinput create device() -> input register device(), which acquires input mutex.
-
device register: input register device() holds input mutex and calls kbd connect() -> input register handle(), which acquires dev->mutex.
-
evdev release: evdev release() calls input flush device() under dev->mutex, which calls input ff flush() acquiring ff->mutex.
Fix this by introducing a new state lock spinlock to protect
udev->state and udev->dev access in uinput request send() instead of
acquiring udev->mutex. The function only needs to atomically check
device state and queue an input event into the ring buffer via
uinput dev event() -- both operations are safe under a spinlock
(ktime get ts64() and wake up interruptible() do not sleep). This
breaks the ff->mutex -> udev->mutex link since a spinlock is a leaf in
the lock ordering and cannot form cycles with mutexes.
To keep state transitions visible to uinput request send(), protect
writes to udev->state in uinput create device() and
uinput destroy device() with the same state lock spinlock.
Additionally, move init completion(&request->done) from
uinput request send() to uinput request submit() before
uinput request reserve slot(). Once the slot is allocated,
uinput flush requests() may call complete() on it at any time from
the destroy path, so the completion must be initialised before the
request becomes visible.
Lock ordering after the fix:
ff->mutex -> state lock (spinlock, leaf)
udev->mutex -> state lock (spinlock, leaf)
udev->mutex -> input mutex -> dev->mutex -> ff->mutex (no back-edge)
Found an issue in the description? Have something to add? Feel free to write us 👾
Related Identifiers
Affected Products
Linux