Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

udmabuf: fix memory leak on last export_udmabuf() error path

In export_udmabuf(), if dma_buf_fd() fails because the FD table is full, a
dma_buf owning the udmabuf has already been created; but the error handling
in udmabuf_create() will tear down the udmabuf without doing anything about
the containing dma_buf.

This leaves a dma_buf in memory that contains a dangling pointer; though
that doesn't seem to lead to anything bad except a memory leak.

Fix it by moving the dma_buf_fd() call out of export_udmabuf() so that we
can give it different error handling.

Note that the shape of this code changed a lot in commit 5e72b2b41a21
("udmabuf: convert udmabuf driver to use folios"); but the memory leak
seems to have existed since the introduction of udmabuf.

Fixes: fbb0de795078 ("Add udmabuf misc device")
Acked-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20241204-udmabuf-fixes-v2-3-23887289de1c@google.com

authored by

Jann Horn and committed by
Vivek Kasireddy
f49856f5 0a16e24e

+17 -11
+17 -11
drivers/dma-buf/udmabuf.c
··· 317 317 return 0; 318 318 } 319 319 320 - static int export_udmabuf(struct udmabuf *ubuf, 321 - struct miscdevice *device, 322 - u32 flags) 320 + static struct dma_buf *export_udmabuf(struct udmabuf *ubuf, 321 + struct miscdevice *device) 323 322 { 324 323 DEFINE_DMA_BUF_EXPORT_INFO(exp_info); 325 - struct dma_buf *buf; 326 324 327 325 ubuf->device = device; 328 326 exp_info.ops = &udmabuf_ops; ··· 328 330 exp_info.priv = ubuf; 329 331 exp_info.flags = O_RDWR; 330 332 331 - buf = dma_buf_export(&exp_info); 332 - if (IS_ERR(buf)) 333 - return PTR_ERR(buf); 334 - 335 - return dma_buf_fd(buf, flags); 333 + return dma_buf_export(&exp_info); 336 334 } 337 335 338 336 static long udmabuf_pin_folios(struct udmabuf *ubuf, struct file *memfd, ··· 385 391 struct folio **folios = NULL; 386 392 pgoff_t pgcnt = 0, pglimit; 387 393 struct udmabuf *ubuf; 394 + struct dma_buf *dmabuf; 388 395 long ret = -EINVAL; 389 396 u32 i, flags; 390 397 ··· 450 455 } 451 456 452 457 flags = head->flags & UDMABUF_FLAGS_CLOEXEC ? O_CLOEXEC : 0; 453 - ret = export_udmabuf(ubuf, device, flags); 454 - if (ret < 0) 458 + dmabuf = export_udmabuf(ubuf, device); 459 + if (IS_ERR(dmabuf)) { 460 + ret = PTR_ERR(dmabuf); 455 461 goto err; 462 + } 463 + /* 464 + * Ownership of ubuf is held by the dmabuf from here. 465 + * If the following dma_buf_fd() fails, dma_buf_put() cleans up both the 466 + * dmabuf and the ubuf (through udmabuf_ops.release). 467 + */ 468 + 469 + ret = dma_buf_fd(dmabuf, flags); 470 + if (ret < 0) 471 + dma_buf_put(dmabuf); 456 472 457 473 kvfree(folios); 458 474 return ret;