If a zero-length bulk or interrupt transfer is requested then assume

USBD_FORCE_SHORT_XFER to ensure that we actually build and execute
a transfer. This means that the various alloc_sqtd_chain functions
will always construct a transfer, so it is safe to modify the
allocated descriptors on return. Previously there were cases where
a zero length transfer would cause a NULL dereference.

Reported by:	bp
This commit is contained in:
Ian Dowse 2006-05-28 23:37:04 +00:00
parent 05c0f5c1e2
commit 46f1e0d36c
3 changed files with 17 additions and 18 deletions

View File

@ -2354,7 +2354,8 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc,
/* BYTES set below */
;
mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize);
forceshort = (xfer->flags & USBD_FORCE_SHORT_XFER) && len % mps == 0;
forceshort = ((xfer->flags & USBD_FORCE_SHORT_XFER) || len == 0) &&
len % mps == 0;
/*
* The control transfer data stage always starts with a toggle of 1.
* For other transfers we let the hardware track the toggle state.

View File

@ -511,7 +511,7 @@ ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc,
int alen, int rd, usbd_xfer_handle xfer,
ohci_soft_td_t *sp, ohci_soft_td_t **ep)
{
ohci_soft_td_t *next, *cur;
ohci_soft_td_t *next, *cur, *end;
ohci_physaddr_t dataphys, physend;
u_int32_t tdflags;
int offset = 0;
@ -523,6 +523,7 @@ ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc,
len = alen;
cur = sp;
end = NULL;
maxp = UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize);
tdflags = htole32(
@ -532,7 +533,7 @@ ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc,
seg = 0;
segoff = 0;
for (;;) {
while (len > 0) {
next = ohci_alloc_std(sc);
if (next == NULL)
goto nomem;
@ -624,21 +625,17 @@ ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc,
cur->xfer = xfer;
DPRINTFN(10,("ohci_alloc_std_chain: cbp=0x%08x be=0x%08x\n",
dataphys, dataphys + curlen - 1));
if (len == 0)
break;
if (len < 0)
panic("Length went negative: %d curlen %d dma %p offset %08x", len, curlen, dma, (int)0);
DPRINTFN(10,("ohci_alloc_std_chain: extend chain\n"));
offset += curlen;
end = cur;
cur = next;
}
if ((flags & USBD_FORCE_SHORT_XFER) &&
if (((flags & USBD_FORCE_SHORT_XFER) || alen == 0) &&
alen % UGETW(opipe->pipe.endpoint->edesc->wMaxPacketSize) == 0) {
/* Force a 0 length transfer at the end. */
cur = next;
next = ohci_alloc_std(sc);
if (next == NULL)
goto nomem;
@ -652,8 +649,9 @@ ohci_alloc_std_chain(struct ohci_pipe *opipe, ohci_softc_t *sc,
cur->flags = 0;
cur->xfer = xfer;
DPRINTFN(2,("ohci_alloc_std_chain: add 0 xfer\n"));
end = cur;
}
*ep = cur;
*ep = end;
return (USBD_NORMAL_COMPLETION);

View File

@ -1766,14 +1766,12 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len,
return (USBD_INVAL);
}
ntd = (len + maxp - 1) / maxp;
if (len == 0)
flags |= USBD_FORCE_SHORT_XFER;
if ((flags & USBD_FORCE_SHORT_XFER) && len % maxp == 0)
ntd++;
DPRINTFN(10, ("uhci_alloc_std_chain: maxp=%d ntd=%d\n", maxp, ntd));
if (ntd == 0) {
*sp = *ep = 0;
DPRINTFN(-1,("uhci_alloc_std_chain: ntd=0\n"));
return (USBD_NORMAL_COMPLETION);
}
KASSERT(ntd > 0, ("uhci_alloc_std_chain: ntd=0"));
tog = upipe->nexttoggle;
prevp = NULL;
startp = NULL;
@ -1811,9 +1809,11 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len,
htole32(rd ? UHCI_TD_IN (l, endpt, addr, tog) :
UHCI_TD_OUT(l, endpt, addr, tog));
KASSERT(seg < dma->nsegs,
KASSERT(seg < dma->nsegs || l == 0,
("uhci_alloc_std_chain: too few segments"));
if (l > dma->segs[seg].ds_len - segoff) {
if (l == 0) {
p->td.td_buffer = 0;
} else if (l > dma->segs[seg].ds_len - segoff) {
/* UHCI can't handle non-contiguous data. */
err = uhci_aux_dma_alloc(sc, p, (char *)xfer->buffer +
i * maxp, l);
@ -1832,7 +1832,7 @@ uhci_alloc_std_chain(struct uhci_pipe *upipe, uhci_softc_t *sc, int len,
segoff);
}
segoff += l;
if (segoff >= dma->segs[seg].ds_len) {
if (l > 0 && segoff >= dma->segs[seg].ds_len) {
KASSERT(segoff == dma->segs[seg].ds_len,
("uhci_alloc_std_chain: overlap"));
if (i * maxp + l != len) {