USB: Check results of dma_map_single
authorLarry Finger <Larry.Finger@lwfinger.net>
Thu, 5 Nov 2009 16:37:03 +0000 (10:37 -0600)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 11 Dec 2009 19:55:20 +0000 (11:55 -0800)
In map_urb_for_dma(), the DMA address returned by dma_map_single()
is not checked to determine if it is legal. This lack of checking
contributed to a problem with the libertas wireless driver
(http://marc.info/?l=linux-wireless&m=125695331205062&w=2). The
difficulty was not detected until the buffer was unmapped. By this time
memory corruption had occurred.

The situation is fixed by testing the returned DMA address, and
returning -EAGAIN if the address is invalid.

Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/hcd.c

index 34de475..026ab2f 100644 (file)
@@ -1275,13 +1275,16 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
 
        if (usb_endpoint_xfer_control(&urb->ep->desc)
            && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
-               if (hcd->self.uses_dma)
+               if (hcd->self.uses_dma) {
                        urb->setup_dma = dma_map_single(
                                        hcd->self.controller,
                                        urb->setup_packet,
                                        sizeof(struct usb_ctrlrequest),
                                        DMA_TO_DEVICE);
-               else if (hcd->driver->flags & HCD_LOCAL_MEM)
+                       if (dma_mapping_error(hcd->self.controller,
+                                               urb->setup_dma))
+                               return -EAGAIN;
+               } else if (hcd->driver->flags & HCD_LOCAL_MEM)
                        ret = hcd_alloc_coherent(
                                        urb->dev->bus, mem_flags,
                                        &urb->setup_dma,
@@ -1293,13 +1296,16 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
        dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
        if (ret == 0 && urb->transfer_buffer_length != 0
            && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
-               if (hcd->self.uses_dma)
+               if (hcd->self.uses_dma) {
                        urb->transfer_dma = dma_map_single (
                                        hcd->self.controller,
                                        urb->transfer_buffer,
                                        urb->transfer_buffer_length,
                                        dir);
-               else if (hcd->driver->flags & HCD_LOCAL_MEM) {
+                       if (dma_mapping_error(hcd->self.controller,
+                                               urb->transfer_dma))
+                               return -EAGAIN;
+               } else if (hcd->driver->flags & HCD_LOCAL_MEM) {
                        ret = hcd_alloc_coherent(
                                        urb->dev->bus, mem_flags,
                                        &urb->transfer_dma,