mirror of
https://git.proxmox.com/git/pve-qemu
synced 2025-08-16 01:58:25 +00:00

Includes fixes for VirtIO-net, ARM and x86(_64) emulation, CVEs to harden NBD server against malicious clients, as well as a few others (VNC, physmem, Intel IOMMU, ...). Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
135 lines
5.2 KiB
Diff
135 lines
5.2 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: David Hildenbrand <david@redhat.com>
|
|
Date: Wed, 28 Aug 2024 11:07:43 +0200
|
|
Subject: [PATCH] softmmu/physmem: fix memory leak in dirty_memory_extend()
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
As reported by Peter, we might be leaking memory when removing the
|
|
highest RAMBlock (in the weird ram_addr_t space), and adding a new one.
|
|
|
|
We will fail to realize that we already allocated bitmaps for more
|
|
dirty memory blocks, and effectively discard the pointers to them.
|
|
|
|
Fix it by getting rid of last_ram_page() and by remembering the number
|
|
of dirty memory blocks that have been allocated already.
|
|
|
|
While at it, let's use "unsigned int" for the number of blocks, which
|
|
should be sufficient until we reach ~32 exabytes.
|
|
|
|
Looks like this leak was introduced as we switched from using a single
|
|
bitmap_zero_extend() to allocating multiple bitmaps:
|
|
bitmap_zero_extend() relies on g_renew() which should have taken care of
|
|
this.
|
|
|
|
Resolves: https://lkml.kernel.org/r/CAFEAcA-k7a+VObGAfCFNygQNfCKL=AfX6A4kScq=VSSK0peqPg@mail.gmail.com
|
|
Reported-by: Peter Maydell <peter.maydell@linaro.org>
|
|
Fixes: 5b82b703b69a ("memory: RCU ram_list.dirty_memory[] for safe RAM hotplug")
|
|
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
Reviewed-by: Peter Xu <peterx@redhat.com>
|
|
Tested-by: Peter Maydell <peter.maydell@linaro.org>
|
|
Cc: qemu-stable@nongnu.org
|
|
Cc: Stefan Hajnoczi <stefanha@redhat.com>
|
|
Cc: Paolo Bonzini <pbonzini@redhat.com>
|
|
Cc: Peter Xu <peterx@redhat.com>
|
|
Cc: "Philippe Mathieu-Daudé" <philmd@linaro.org>
|
|
Signed-off-by: David Hildenbrand <david@redhat.com>
|
|
(picked from https://lore.kernel.org/qemu-devel/20240828090743.128647-1-david@redhat.com/)
|
|
[FE: backport - remove not-yet-existing variable in context of hunk touching ram_block_add()]
|
|
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
|
|
---
|
|
include/exec/ramlist.h | 1 +
|
|
system/physmem.c | 35 +++++++++--------------------------
|
|
2 files changed, 10 insertions(+), 26 deletions(-)
|
|
|
|
diff --git a/include/exec/ramlist.h b/include/exec/ramlist.h
|
|
index 2ad2a81acc..d9cfe530be 100644
|
|
--- a/include/exec/ramlist.h
|
|
+++ b/include/exec/ramlist.h
|
|
@@ -50,6 +50,7 @@ typedef struct RAMList {
|
|
/* RCU-enabled, writes protected by the ramlist lock. */
|
|
QLIST_HEAD(, RAMBlock) blocks;
|
|
DirtyMemoryBlocks *dirty_memory[DIRTY_MEMORY_NUM];
|
|
+ unsigned int num_dirty_blocks;
|
|
uint32_t version;
|
|
QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers;
|
|
} RAMList;
|
|
diff --git a/system/physmem.c b/system/physmem.c
|
|
index a4fe3d2bf8..78f7db1121 100644
|
|
--- a/system/physmem.c
|
|
+++ b/system/physmem.c
|
|
@@ -1497,18 +1497,6 @@ static ram_addr_t find_ram_offset(ram_addr_t size)
|
|
return offset;
|
|
}
|
|
|
|
-static unsigned long last_ram_page(void)
|
|
-{
|
|
- RAMBlock *block;
|
|
- ram_addr_t last = 0;
|
|
-
|
|
- RCU_READ_LOCK_GUARD();
|
|
- RAMBLOCK_FOREACH(block) {
|
|
- last = MAX(last, block->offset + block->max_length);
|
|
- }
|
|
- return last >> TARGET_PAGE_BITS;
|
|
-}
|
|
-
|
|
static void qemu_ram_setup_dump(void *addr, ram_addr_t size)
|
|
{
|
|
int ret;
|
|
@@ -1762,13 +1750,11 @@ void qemu_ram_msync(RAMBlock *block, ram_addr_t start, ram_addr_t length)
|
|
}
|
|
|
|
/* Called with ram_list.mutex held */
|
|
-static void dirty_memory_extend(ram_addr_t old_ram_size,
|
|
- ram_addr_t new_ram_size)
|
|
+static void dirty_memory_extend(ram_addr_t new_ram_size)
|
|
{
|
|
- ram_addr_t old_num_blocks = DIV_ROUND_UP(old_ram_size,
|
|
- DIRTY_MEMORY_BLOCK_SIZE);
|
|
- ram_addr_t new_num_blocks = DIV_ROUND_UP(new_ram_size,
|
|
- DIRTY_MEMORY_BLOCK_SIZE);
|
|
+ unsigned int old_num_blocks = ram_list.num_dirty_blocks;
|
|
+ unsigned int new_num_blocks = DIV_ROUND_UP(new_ram_size,
|
|
+ DIRTY_MEMORY_BLOCK_SIZE);
|
|
int i;
|
|
|
|
/* Only need to extend if block count increased */
|
|
@@ -1800,6 +1786,8 @@ static void dirty_memory_extend(ram_addr_t old_ram_size,
|
|
g_free_rcu(old_blocks, rcu);
|
|
}
|
|
}
|
|
+
|
|
+ ram_list.num_dirty_blocks = new_num_blocks;
|
|
}
|
|
|
|
static void ram_block_add(RAMBlock *new_block, Error **errp)
|
|
@@ -1808,11 +1796,9 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
|
|
const bool shared = qemu_ram_is_shared(new_block);
|
|
RAMBlock *block;
|
|
RAMBlock *last_block = NULL;
|
|
- ram_addr_t old_ram_size, new_ram_size;
|
|
+ ram_addr_t ram_size;
|
|
Error *err = NULL;
|
|
|
|
- old_ram_size = last_ram_page();
|
|
-
|
|
qemu_mutex_lock_ramlist();
|
|
new_block->offset = find_ram_offset(new_block->max_length);
|
|
|
|
@@ -1840,11 +1826,8 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
|
|
}
|
|
}
|
|
|
|
- new_ram_size = MAX(old_ram_size,
|
|
- (new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS);
|
|
- if (new_ram_size > old_ram_size) {
|
|
- dirty_memory_extend(old_ram_size, new_ram_size);
|
|
- }
|
|
+ ram_size = (new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS;
|
|
+ dirty_memory_extend(ram_size);
|
|
/* Keep the list sorted from biggest to smallest block. Unlike QTAILQ,
|
|
* QLIST (which has an RCU-friendly variant) does not have insertion at
|
|
* tail, so save the last element in last_block.
|