udmabuf_create() derives the number of pages to allocate directly from
the size supplied by userspace, bounded only by the size_limit_mb module
parameter. Since commit 44e9eb5a7621 ("dma-buf/udmabuf: Disable the size
limit by default") that parameter defaults to INT_MAX, which puts the
page count limit at roughly 5.5e11 pages, i.e. no limit in practice.

init_udmabuf() then asks kvmalloc_objs() for pgcnt * sizeof(struct page *)
bytes. __kvmalloc_node_noprof() refuses any request larger than INT_MAX
and splats WARN_ON_ONCE(!(flags & __GFP_NOWARN)) on its way out, while
kvmalloc_objs() defaults to a plain GFP_KERNEL.

A single UDMABUF_CREATE against /dev/udmabuf with a sufficiently large
size is therefore all it takes to trigger:

  ------------[ cut here ]------------
  WARNING: mm/slub.c:7022 at __kvmalloc_node_noprof+0x412/0x5a0
  Call Trace:
   udmabuf_create+0x11f/0x540
   udmabuf_ioctl+0xf1/0x190
   __x64_sys_ioctl+0x91/0xe0
   do_syscall_64+0xdd/0x4a0

The size is entirely userspace-controlled and allocation failure is
already handled by returning -ENOMEM to the caller, so this is not a
condition the kernel needs to complain about. Pass __GFP_NOWARN and let
the existing error path do its job.

Note that capping the limit instead would change the error reported to
userspace from -ENOMEM to -EINVAL and would reintroduce a fixed ceiling
that the above commit deliberately removed, so __GFP_NOWARN seems the
more faithful fix.

Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=bfeed181d1ce9ec87a8f
Fixes: 44e9eb5a7621 ("dma-buf/udmabuf: Disable the size limit by default")
Signed-off-by: Priyanka Mani <[email protected]>
---
 drivers/dma-buf/udmabuf.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
index df6dd0046242..127369e502a3 100644
--- a/drivers/dma-buf/udmabuf.c
+++ b/drivers/dma-buf/udmabuf.c
@@ -190,11 +190,13 @@ static void unpin_all_folios(struct udmabuf *ubuf)
 
 static __always_inline int init_udmabuf(struct udmabuf *ubuf, pgoff_t pgcnt)
 {
-       ubuf->pages = kvmalloc_objs(*ubuf->pages, pgcnt);
+       ubuf->pages = kvmalloc_objs(*ubuf->pages, pgcnt,
+                                   GFP_KERNEL | __GFP_NOWARN);
        if (!ubuf->pages)
                return -ENOMEM;
 
-       ubuf->pinned_folios = kvmalloc_objs(*ubuf->pinned_folios, pgcnt);
+       ubuf->pinned_folios = kvmalloc_objs(*ubuf->pinned_folios, pgcnt,
+                                           GFP_KERNEL | __GFP_NOWARN);
        if (!ubuf->pinned_folios)
                return -ENOMEM;
 
-- 
2.43.0

Reply via email to