aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorHollin <hollinisme@gmail.com>2026-04-17 04:36:41 +0800
committerGitHub <noreply@github.com>2026-04-16 21:36:41 +0100
commit4d9f663450fa80ff375612dbbafe073700e3d3d8 (patch)
tree9b83eeaecae8a43c03628713c3c8d0d97b852f5e /lib
parent2311cde4a933dc62ee65b4fe8f408e37bf2bb390 (diff)
downloadlibvfio-user-master.tar.gz
libvfio-user-master.tar.bz2
libvfio-user-master.zip
Free dirty_bitmap when unmapping DMA region to fix memory leak (#847)HEADmaster
The dirty bitmap was allocated when dirty tracking is enabled for a DMA region, but was not released during region teardown. This could lead to memory leaks over time with repeated DMA map/unmap operations while dirty page logging is active. Signed-off-by: liuhaolin <hollinisme@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/dma.c50
1 files changed, 30 insertions, 20 deletions
diff --git a/lib/dma.c b/lib/dma.c
index 334d258..724c170 100644
--- a/lib/dma.c
+++ b/lib/dma.c
@@ -66,6 +66,32 @@ fd_get_blocksize(int fd)
return st.st_blksize;
}
+static int
+dirty_page_logging_start_on_region(dma_memory_region_t *region, size_t pgsize)
+{
+ assert(region->fd != -1);
+
+ ssize_t size = get_bitmap_size(region->info.iova.iov_len, pgsize);
+ if (size < 0) {
+ return size;
+ }
+
+ region->dirty_bitmap = calloc(size, 1);
+ if (region->dirty_bitmap == NULL) {
+ return ERROR_INT(errno);
+ }
+ return 0;
+}
+
+static void
+dirty_page_logging_stop_on_region(dma_memory_region_t *region)
+{
+ if (region->dirty_bitmap != NULL) {
+ free(region->dirty_bitmap);
+ region->dirty_bitmap = NULL;
+ }
+}
+
dma_controller_t *
dma_controller_create(vfu_ctx_t *vfu_ctx, size_t max_regions, size_t max_size)
{
@@ -135,6 +161,8 @@ MOCK_DEFINE(dma_controller_unmap_region)(dma_controller_t *dma,
assert(region->fd != -1);
+ dirty_page_logging_stop_on_region(region);
+
err = fd_cache_put(&region->fd);
assert(err == 0);
}
@@ -258,23 +286,6 @@ dma_map_region(dma_controller_t *dma, dma_memory_region_t *region)
return 0;
}
-static int
-dirty_page_logging_start_on_region(dma_memory_region_t *region, size_t pgsize)
-{
- assert(region->fd != -1);
-
- ssize_t size = get_bitmap_size(region->info.iova.iov_len, pgsize);
- if (size < 0) {
- return size;
- }
-
- region->dirty_bitmap = calloc(size, 1);
- if (region->dirty_bitmap == NULL) {
- return ERROR_INT(errno);
- }
- return 0;
-}
-
dma_memory_region_t *
MOCK_DEFINE(dma_controller_add_region)(dma_controller_t *dma,
vfu_dma_addr_t dma_addr, uint64_t size,
@@ -413,7 +424,7 @@ rollback:
if (region->info.vaddr != NULL) {
dma_controller_unmap_region(dma, region);
}
- free(region->dirty_bitmap);
+ dirty_page_logging_stop_on_region(region);
free(region);
}
err = fd_cache_put(&fd);
@@ -475,8 +486,7 @@ dma_controller_dirty_page_logging_reset(dma_controller_t *dma)
for (btree_iter_init(&dma->regions, 0, &iter);
(region = btree_iter_get(&iter, NULL)) != NULL;
btree_iter_next(&iter)) {
- free(region->dirty_bitmap);
- region->dirty_bitmap = NULL;
+ dirty_page_logging_stop_on_region(region);
}
dma->dirty_pgsize = 0;
}