Skip to content

Commit 0143d14

Browse files
lrh2000gregkh
authored andcommitted
usb: usbfs: Enforce page requirements for mmap
The current implementation of usbdev_mmap uses usb_alloc_coherent to allocate memory pages that will later be mapped into the user space. Meanwhile, usb_alloc_coherent employs three different methods to allocate memory, as outlined below: * If hcd->localmem_pool is non-null, it uses gen_pool_dma_alloc to allocate memory; * If DMA is not available, it uses kmalloc to allocate memory; * Otherwise, it uses dma_alloc_coherent. However, it should be noted that gen_pool_dma_alloc does not guarantee that the resulting memory will be page-aligned. Furthermore, trying to map slab pages (i.e., memory allocated by kmalloc) into the user space is not resonable and can lead to problems, such as a type confusion bug when PAGE_TABLE_CHECK=y [1]. To address these issues, this patch introduces hcd_alloc_coherent_pages, which addresses the above two problems. Specifically, hcd_alloc_coherent_pages uses gen_pool_dma_alloc_align instead of gen_pool_dma_alloc to ensure that the memory is page-aligned. To replace kmalloc, hcd_alloc_coherent_pages directly allocates pages by calling __get_free_pages. Reported-by: [email protected] Closes: https://lore.kernel.org/lkml/[email protected]/ [1] Fixes: f7d34b4 ("USB: Add support for usbfs zerocopy.") Fixes: ff2437b ("usb: host: Fix excessive alignment restriction for local memory allocations") Cc: [email protected] Signed-off-by: Ruihan Li <[email protected]> Acked-by: Alan Stern <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 7b32040 commit 0143d14

File tree

3 files changed

+51
-4
lines changed

3 files changed

+51
-4
lines changed

drivers/usb/core/buffer.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,44 @@ void hcd_buffer_free(
172172
}
173173
dma_free_coherent(hcd->self.sysdev, size, addr, dma);
174174
}
175+
176+
void *hcd_buffer_alloc_pages(struct usb_hcd *hcd,
177+
size_t size, gfp_t mem_flags, dma_addr_t *dma)
178+
{
179+
if (size == 0)
180+
return NULL;
181+
182+
if (hcd->localmem_pool)
183+
return gen_pool_dma_alloc_align(hcd->localmem_pool,
184+
size, dma, PAGE_SIZE);
185+
186+
/* some USB hosts just use PIO */
187+
if (!hcd_uses_dma(hcd)) {
188+
*dma = DMA_MAPPING_ERROR;
189+
return (void *)__get_free_pages(mem_flags,
190+
get_order(size));
191+
}
192+
193+
return dma_alloc_coherent(hcd->self.sysdev,
194+
size, dma, mem_flags);
195+
}
196+
197+
void hcd_buffer_free_pages(struct usb_hcd *hcd,
198+
size_t size, void *addr, dma_addr_t dma)
199+
{
200+
if (!addr)
201+
return;
202+
203+
if (hcd->localmem_pool) {
204+
gen_pool_free(hcd->localmem_pool,
205+
(unsigned long)addr, size);
206+
return;
207+
}
208+
209+
if (!hcd_uses_dma(hcd)) {
210+
free_pages((unsigned long)addr, get_order(size));
211+
return;
212+
}
213+
214+
dma_free_coherent(hcd->self.sysdev, size, addr, dma);
215+
}

drivers/usb/core/devio.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ static int connected(struct usb_dev_state *ps)
186186
static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count)
187187
{
188188
struct usb_dev_state *ps = usbm->ps;
189+
struct usb_hcd *hcd = bus_to_hcd(ps->dev->bus);
189190
unsigned long flags;
190191

191192
spin_lock_irqsave(&ps->lock, flags);
@@ -194,8 +195,8 @@ static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count)
194195
list_del(&usbm->memlist);
195196
spin_unlock_irqrestore(&ps->lock, flags);
196197

197-
usb_free_coherent(ps->dev, usbm->size, usbm->mem,
198-
usbm->dma_handle);
198+
hcd_buffer_free_pages(hcd, usbm->size,
199+
usbm->mem, usbm->dma_handle);
199200
usbfs_decrease_memory_usage(
200201
usbm->size + sizeof(struct usb_memory));
201202
kfree(usbm);
@@ -247,8 +248,8 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma)
247248
goto error_decrease_mem;
248249
}
249250

250-
mem = usb_alloc_coherent(ps->dev, size, GFP_USER | __GFP_NOWARN,
251-
&dma_handle);
251+
mem = hcd_buffer_alloc_pages(hcd,
252+
size, GFP_USER | __GFP_NOWARN, &dma_handle);
252253
if (!mem) {
253254
ret = -ENOMEM;
254255
goto error_free_usbm;

include/linux/usb/hcd.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,11 @@ void *hcd_buffer_alloc(struct usb_bus *bus, size_t size,
501501
void hcd_buffer_free(struct usb_bus *bus, size_t size,
502502
void *addr, dma_addr_t dma);
503503

504+
void *hcd_buffer_alloc_pages(struct usb_hcd *hcd,
505+
size_t size, gfp_t mem_flags, dma_addr_t *dma);
506+
void hcd_buffer_free_pages(struct usb_hcd *hcd,
507+
size_t size, void *addr, dma_addr_t dma);
508+
504509
/* generic bus glue, needed for host controllers that don't use PCI */
505510
extern irqreturn_t usb_hcd_irq(int irq, void *__hcd);
506511

0 commit comments

Comments
 (0)