Source-Changes-D archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: kern/56194: xhci panic: kernel diagnostic assertion "xs->xs_xr[dci] == NULL" failed



> Module Name:    src
> Committed By:   mlelstv
> Date:           Sat Jan 10 08:54:08 UTC 2026
> 
> Modified Files:
>         src/sys/dev/usb: uaudio.c
> 
> Log Message:
> Also use atomic operation to store pipe pointers, not just to clear them.
> 
> Probably fixes PR 56194.
> 
> 
> To generate a diff of this commit:
> cvs rdiff -u -r1.184 -r1.185 src/sys/dev/usb/uaudio.c

I think this cannot be correct.

There are several possible scenarios:

1. Concurrent calls to uaudio_chan_open and/or uaudio_chan_close with
   the same channel are NOT possible.  I.e., all such calls are
   serialized.

   => In this case, the atomic_swap_ptr can't make any difference, so
      this patch can't fix anything.

2. Concurrent calls to uaudio_chan_open with the same channel are
   possible.

   => In this case, the following sequence is possible:

        thread0                 thread1
        -------                 -------
        usbd_open_pipe(&pipe0)  usbd_open_pipe(&pipe1)
        pipe0 = atomic_swap_ptr(&ch->pipe, pipe0)
        KASSERT(pipe0 == NULL)
                                pipe1 = atomic_swap_ptr(&ch->pipe, pipe1)
                                KASSERT(pipe1 == NULL)

      The KASSERT in thread0 succeeds but the KASSERT in thread1 fails
      (or, in non-DIAGNOSTIC builds, this leaks a pipe).  So this is
      still broken.

3. A call to uaudio_chan_open can happen concurrently with a call to
   uaudio_chan_close with the same channel.

   => In this case, the following sequence is possible:

        thread0                 thread1
        -------                 -------
                                uaudio_trigger_input(...)
                                  uaudio_chan_open(sc, ch)
                                    usbd_open_pipe(&pipe)
                                    atomic_swap_ptr(&ch->pipe, pipe)
        uaudio_chan_close(sc, ch)
          pipe = atomic_swap_ptr(&ch->pipe, NULL)
          usbd_close_pipe(pipe)
                                  uaudio_chan_alloc_buffers(sc, ch)
                                    usbd_create_xfer(ch->pipe, ...)

      At this point in thread1, ch->pipe is null, so we have a null
      pointer dereference.  (Another possible ordering is that thread1
      loads ch->pipe before thread0 nulls it out, but then thread0
      closes the pipe before thread1 enters usbd_create_xfer, and so
      it'll be use-after-free instead of null pointer dereference.)
      So again, this is still broken.

I suspect that both (2) and (3) are true, but that really (1) _ought_
to be true.

If so, perhaps we can enforce it by a per-channel mutex taken by

uaudio_halt_in_dma_unlocked
uaudio_halt_out_dma_unlocked
uaudio_trigger_input
uaudio_trigger_output

and asserted held by

uaudio_chan_set_param
uaudio_chan_open
uaudio_chan_alloc_buffers
uaudio_chan_free_buffers
uaudio_chan_close

and then dispense with the atomic_swap_ptrs altogether.


Home | Main Index | Thread Index | Old Index