On 4/29/19 1:42 PM, Longpeng (Mike) wrote:
> Hi Philippe,
> 
> On 2019/4/29 19:16, Philippe Mathieu-Daudé wrote:
> 
>> Hi Mike,
>>
>> On 4/29/19 9:39 AM, Longpeng(Mike) wrote:
>>> From: Longpeng <longpe...@huawei.com>
>>>
>>> we found the following core in our environment:
>>> 0  0x00007fc6b06c2237 in raise ()
>>> 1  0x00007fc6b06c3928 in abort ()
>>> 2  0x00007fc6b06bb056 in __assert_fail_base ()
>>> 3  0x00007fc6b06bb102 in __assert_fail ()
>>> 4  0x0000000000702e36 in xhci_kick_ep (...)
>>
>>   5 xhci_doorbell_write?
>>
>>> 6  0x000000000047767f in access_with_adjusted_size (...)
>>> 7  0x000000000047944d in memory_region_dispatch_write (...)
>>> 8  0x000000000042df17 in address_space_write_continue (...)
>>> 10 0x000000000043084d in address_space_rw (...)
>>> 11 0x000000000047451b in kvm_cpu_exec (cpu=cpu@entry=0x1ab11b0)
>>> 12 0x000000000045dcf5 in qemu_kvm_cpu_thread_fn (arg=0x1ab11b0)
>>> 13 0x0000000000870631 in qemu_thread_start (args=args@entry=0x1acfb50)
>>> 14 0x00000000008959a7 in thread_entry_for_hotfix (pthread_cb=<optimized 
>>> out>)
>>> 15 0x00007fc6b0a60dd5 in start_thread ()
>>> 16 0x00007fc6b078a59d in clone ()
>>> (gdb) bt
>>> (gdb) f 5
>>
>> This is the frame you removed...
>>
>>> (gdb) p /x tmp
>>> $9 = 0x62481a00 <-- last byte 0x00 is @epid
>>
>> I don't see 'tmp' in xhci_doorbell_write().
>>
>> Can you use trace events?
>>
>> There we have trace_usb_xhci_doorbell_write().
>>
> 
> Sorry , I'm careless to remove the important information.
> 
> 
> This is our whole frame:
> 
> (gdb) bt
> #0  0x00007fc6b06c2237 in raise () from /usr/lib64/libc.so.6
> #1  0x00007fc6b06c3928 in abort () from /usr/lib64/libc.so.6
> #2  0x00007fc6b06bb056 in __assert_fail_base () from /usr/lib64/libc.so.6
> #3  0x00007fc6b06bb102 in __assert_fail () from /usr/lib64/libc.so.6
> #4  0x0000000000702e36 in xhci_kick_ep (...)
> #5  0x000000000047897a in memory_region_write_accessor (...)
> #6  0x000000000047767f in access_with_adjusted_size (...)
> #7  0x000000000047944d in memory_region_dispatch_write
> (mr=mr@entry=0x7fc6a0138df0, addr=addr@entry=156, data=1648892416,
> size=size@entry=4, attrs=attrs@entry=...)

So this is a 32-bit access, to address 156 (which is the slotid) and
data=1648892416=0x62481a00 indeed.

But watch out access_with_adjusted_size() calls adjust_endianness()...

> #8  0x000000000042df17 in address_space_write_continue (...)
> #9  0x00000000004302d5 in address_space_write (...)
> #10 0x000000000043084d in address_space_rw (...)
> #11 0x000000000047451b in kvm_cpu_exec (...)
> #12 0x000000000045dcf5 in qemu_kvm_cpu_thread_fn (arg=0x1ab11b0)
> #13 0x0000000000870631 in qemu_thread_start (args=args@entry=0x1acfb50)
> #14 0x00000000008959a7 in thread_entry_for_hotfix (pthread_cb=<optimized out>)
> #15 0x00007fc6b0a60dd5 in start_thread () from /usr/lib64/libpthread.so.0
> #16 0x00007fc6b078a59d in clone () from /usr/lib64/libc.so.6
> 
> (gdb) f 5
> #5  0x000000000047897a in memory_region_write_accessor (...)
> 529       mr->ops->write(mr->opaque, addr, tmp, size);
> (gdb) p /x tmp
> $9 = 0x62481a00

... since memory_region_write_accessor() has the same argument, then I
can assume your guest is running in Little-Endian.

> static void xhci_doorbell_write(void *ptr, hwaddr reg,
>                                 uint64_t val, unsigned size)
> So, the @val is 0x62481a00, and the last byte is epid, right?
> 
>>>
>>> xhci_doorbell_write() already check the upper bound of @slotid an @epid,
>>> it also need to check the lower bound.
>>>
>>> Cc: Gonglei <arei.gong...@huawei.com>
>>> Signed-off-by: Longpeng <longpe...@huawei.com>
>>> ---
>>>  hw/usb/hcd-xhci.c | 4 ++--
>>>  1 file changed, 2 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
>>> index ec28bee..b4e6bfc 100644
>>> --- a/hw/usb/hcd-xhci.c
>>> +++ b/hw/usb/hcd-xhci.c
>>> @@ -3135,9 +3135,9 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
>>
>> Expanding the diff:
>>
>>        if (reg == 0) {
>>            if (val == 0) {
>>                xhci_process_commands(xhci);
>>            } else {
>>                DPRINTF("xhci: bad doorbell 0 write: 0x%x\n",
>>                        (uint32_t)val);
>>            }
>>>      } else {
>>>          epid = val & 0xff;
>>>          streamid = (val >> 16) & 0xffff;
>>> -        if (reg > xhci->numslots) {
>>> +        if (reg == 0 || reg > xhci->numslots) {
>>
>> So 'reg' can not be zero here...
>>
> 
> Oh, you're right.
> 
>>>              DPRINTF("xhci: bad doorbell %d\n", (int)reg);
>>> -        } else if (epid > 31) {
>>> +        } else if (epid == 0 || epid > 31) {
>>
>> Here neither.
>>
> 
> In our frame, the epid is zero. The @val is from guest which is untrusted, 
> when
> this problem happened, I saw it wrote many invalid value, not only usb but 
> also
> other devices.

If you use mainstream QEMU, we have:

static void qemu_xhci_instance_init(Object *obj)
{
    ...
    xhci->numslots = MAXSLOTS;
    ...
}

$ git grep define.*MAXSLOTS
hw/usb/hcd-xhci.c:52:#define LEN_DOORBELL    ((MAXSLOTS + 1) * 0x20)
hw/usb/hcd-xhci.h:33:#define MAXSLOTS 64
hw/usb/hcd-xhci.h:37:#define EV_QUEUE (((3 * 24) + 16) * MAXSLOTS)

> 
>>>              DPRINTF("xhci: bad doorbell %d write: 0x%x\n",
>>>                      (int)reg, (uint32_t)val);
>>>          } else {
>>>
>>
>> Isn't it the other line that triggered the assertion?
>>
>> static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
>>                          unsigned int epid, unsigned int streamid)
>> {
>>     XHCIEPContext *epctx;
>>
>>     assert(slotid >= 1 && slotid <= xhci->numslots); // <===

slotid >= 1 // true
slotid <= xhci->numslots // FALSE (156 > 64)

So this assertion won't pass.

>>     assert(epid >= 1 && epid <= 31);
>>
>> Regards,
>>
>> Phil.
>>

Reply via email to