mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 20:51:03 +02:00
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: syzbot+fcf1a817ceb50935ce99@syzkaller.appspotmail.comm Closes: https://lore.kernel.org/lkml/000000000000258e5e05fae79fc1@google.com/ [1] Fixes:f7d34b445a
("USB: Add support for usbfs zerocopy.") Fixes:ff2437befd
("usb: host: Fix excessive alignment restriction for local memory allocations") Cc: stable@vger.kernel.org Signed-off-by: Ruihan Li <lrh2000@pku.edu.cn> Acked-by: Alan Stern <stern@rowland.harvard.edu> Link: https://lore.kernel.org/r/20230515130958.32471-2-lrh2000@pku.edu.cn Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
7b32040f6d
commit
0143d148d1
@@ -172,3 +172,44 @@ void hcd_buffer_free(
|
|||||||
}
|
}
|
||||||
dma_free_coherent(hcd->self.sysdev, size, addr, dma);
|
dma_free_coherent(hcd->self.sysdev, size, addr, dma);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *hcd_buffer_alloc_pages(struct usb_hcd *hcd,
|
||||||
|
size_t size, gfp_t mem_flags, dma_addr_t *dma)
|
||||||
|
{
|
||||||
|
if (size == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (hcd->localmem_pool)
|
||||||
|
return gen_pool_dma_alloc_align(hcd->localmem_pool,
|
||||||
|
size, dma, PAGE_SIZE);
|
||||||
|
|
||||||
|
/* some USB hosts just use PIO */
|
||||||
|
if (!hcd_uses_dma(hcd)) {
|
||||||
|
*dma = DMA_MAPPING_ERROR;
|
||||||
|
return (void *)__get_free_pages(mem_flags,
|
||||||
|
get_order(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dma_alloc_coherent(hcd->self.sysdev,
|
||||||
|
size, dma, mem_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hcd_buffer_free_pages(struct usb_hcd *hcd,
|
||||||
|
size_t size, void *addr, dma_addr_t dma)
|
||||||
|
{
|
||||||
|
if (!addr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (hcd->localmem_pool) {
|
||||||
|
gen_pool_free(hcd->localmem_pool,
|
||||||
|
(unsigned long)addr, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hcd_uses_dma(hcd)) {
|
||||||
|
free_pages((unsigned long)addr, get_order(size));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_free_coherent(hcd->self.sysdev, size, addr, dma);
|
||||||
|
}
|
||||||
|
@@ -186,6 +186,7 @@ static int connected(struct usb_dev_state *ps)
|
|||||||
static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count)
|
static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count)
|
||||||
{
|
{
|
||||||
struct usb_dev_state *ps = usbm->ps;
|
struct usb_dev_state *ps = usbm->ps;
|
||||||
|
struct usb_hcd *hcd = bus_to_hcd(ps->dev->bus);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&ps->lock, flags);
|
spin_lock_irqsave(&ps->lock, flags);
|
||||||
@@ -194,8 +195,8 @@ static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count)
|
|||||||
list_del(&usbm->memlist);
|
list_del(&usbm->memlist);
|
||||||
spin_unlock_irqrestore(&ps->lock, flags);
|
spin_unlock_irqrestore(&ps->lock, flags);
|
||||||
|
|
||||||
usb_free_coherent(ps->dev, usbm->size, usbm->mem,
|
hcd_buffer_free_pages(hcd, usbm->size,
|
||||||
usbm->dma_handle);
|
usbm->mem, usbm->dma_handle);
|
||||||
usbfs_decrease_memory_usage(
|
usbfs_decrease_memory_usage(
|
||||||
usbm->size + sizeof(struct usb_memory));
|
usbm->size + sizeof(struct usb_memory));
|
||||||
kfree(usbm);
|
kfree(usbm);
|
||||||
@@ -247,8 +248,8 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma)
|
|||||||
goto error_decrease_mem;
|
goto error_decrease_mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
mem = usb_alloc_coherent(ps->dev, size, GFP_USER | __GFP_NOWARN,
|
mem = hcd_buffer_alloc_pages(hcd,
|
||||||
&dma_handle);
|
size, GFP_USER | __GFP_NOWARN, &dma_handle);
|
||||||
if (!mem) {
|
if (!mem) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto error_free_usbm;
|
goto error_free_usbm;
|
||||||
|
@@ -501,6 +501,11 @@ void *hcd_buffer_alloc(struct usb_bus *bus, size_t size,
|
|||||||
void hcd_buffer_free(struct usb_bus *bus, size_t size,
|
void hcd_buffer_free(struct usb_bus *bus, size_t size,
|
||||||
void *addr, dma_addr_t dma);
|
void *addr, dma_addr_t dma);
|
||||||
|
|
||||||
|
void *hcd_buffer_alloc_pages(struct usb_hcd *hcd,
|
||||||
|
size_t size, gfp_t mem_flags, dma_addr_t *dma);
|
||||||
|
void hcd_buffer_free_pages(struct usb_hcd *hcd,
|
||||||
|
size_t size, void *addr, dma_addr_t dma);
|
||||||
|
|
||||||
/* generic bus glue, needed for host controllers that don't use PCI */
|
/* generic bus glue, needed for host controllers that don't use PCI */
|
||||||
extern irqreturn_t usb_hcd_irq(int irq, void *__hcd);
|
extern irqreturn_t usb_hcd_irq(int irq, void *__hcd);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user