[PATCH RFC] usb: adutux: fix unlocked read_buffer_length write in adu_open() (data race)

From: Kanishka De Silva

Date: Mon Jul 13 2026 - 00:47:13 EST


Hi Greg,

While auditing drivers/usb/misc/adutux.c in Linux 7.1.3, I found that
adu_open() writes dev->read_buffer_length without holding
dev->buflock, while every other path that touches this field
(adu_interrupt_in_callback() and adu_read()) correctly takes buflock
as documented in the locking comment at the top of the file:

* The locking scheme is a vanilla 3-lock:
* adu_device.buflock: A spinlock, covers what IRQs touch.

adu_open() (drivers/usb/misc/adutux.c:268):

/* initialize in direction */
dev->read_buffer_length = 0;

This is reached while holding only adutux_mutex, not buflock. Meanwhile:

- adu_interrupt_in_callback() (lines 165-196) holds buflock while
reading and incrementing read_buffer_length from IRQ context.
- adu_read() (lines 394-404) holds buflock while reading, swapping,
and zeroing read_buffer_length.

So two different locks (adutux_mutex vs buflock) are used to protect
the same field, which is a textbook data race: if an IRQ callback
increments read_buffer_length concurrently with a re-open() clearing
it, the increment can be silently lost, corrupting buffer state.
Repeated open/close under interrupt load can produce stale or dropped
USB read data from the ADU device.

I reproduced the race by extracting the exact code paths (locking
primitives, field layout, and control flow) into a standalone
userspace program and running it under ThreadSanitizer with pthreads
simulating the IRQ callback and open() paths concurrently. TSan
reports the expected data race between the two lock domains on
read_buffer_length. I'm happy to send the verifier program if useful,
though I want to be clear it's a userspace reconstruction for
lock-discipline verification, not the compiled kernel driver itself.

Suggested fix:

static int adu_open(struct inode *inode, struct file *file)
{
+ unsigned long flags;
...
- dev->read_buffer_length = 0;
+ spin_lock_irqsave(&dev->buflock, flags);
+ dev->read_buffer_length = 0;
+ spin_unlock_irqrestore(&dev->buflock, flags);

Impact is data corruption / stale reads on the ADU USB device class,
not memory corruption or privilege escalation, so I'd call this Medium
severity. Reachable by any unprivileged user who can open
/dev/usb/adutuxN.

Happy to send a proper patch with commit message if this looks correct
to you — let me know if you'd like it in a different form.

Thanks,
Kanishka
HEXER365 / github.com/hexer365