The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH RFC] usb: adutux: fix unlocked read_buffer_length write in adu_open() (data race)
@ 2026-07-13  4:45 Kanishka De Silva
  2026-07-13  5:15 ` Greg KH
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Kanishka De Silva @ 2026-07-13  4:45 UTC (permalink / raw)
  To: gregkh; +Cc: linux-usb, linux-kernel

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-07-13 13:48 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13  4:45 [PATCH RFC] usb: adutux: fix unlocked read_buffer_length write in adu_open() (data race) Kanishka De Silva
2026-07-13  5:15 ` Greg KH
2026-07-13  5:37   ` [PATCH] usb: adutux: fix unlocked read_buffer_length write in adu_open() Kanishka De Silva
2026-07-13  7:25 ` [PATCH RFC] usb: adutux: fix unlocked read_buffer_length write in adu_open() (data race) Oliver Neukum
2026-07-13  7:56 ` [PATCH v2] usb: adutux: take buflock when resetting read_buffer_length in adu_open() Kanishka De Silva
2026-07-13 12:03   ` Greg KH
2026-07-13 13:48     ` Alan Stern

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox