- tests: bump QOS_PATH_MAX_ELEMENT_SIZE again - softmmu/physmem: fix memory leak in dirty_memory_extend() - crypto: run qcrypto_pbkdf2_count_iters in a new thread - hw/audio/virtio-sound: fix heap buffer overflow - hw/intc/arm_gic: fix spurious level triggered interrupts - ui/sdl2: set swap interval explicitly when OpenGL is enabled - target/riscv/kvm: tolerate KVM disable ext errors - virtio: remove virtio_tswap16s() call in vring_packed_event_read() - block: fix -Werror=maybe-uninitialized false-positive - hw/remote/vfio-user: Fix config space access byte order - hw/loongarch/virt: Fix memory leak - hw/intc/riscv_aplic: APLICs should add child earlier than realize - stdvga: fix screen blanking - ui/gtk: Draw guest frame at refresh cycle - target/i386: fix size of EBP writeback in gen_enter() - virtio-net: drop too short packets early - target/ppc: Fix lxv/stxv MSR facility check - target/ppc: Fix lxvx/stxvx facility check - virtio-snd: add max size bounds check in input cb(CVE-2024-7730) Signed-off-by: Jiabo Feng <fengjiabo1@huawei.com> (cherry picked from commit e2eb79f1867bb8d8d870e758f06d2a32b3a4fc8a)
140 lines
5.3 KiB
Diff
140 lines
5.3 KiB
Diff
From c6d6cbb2c33c3c7b2574c3baa2d2477d9d4ac91c 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>
|
|
Link: https://lore.kernel.org/r/20240828090743.128647-1-david@redhat.com
|
|
Signed-off-by: Peter Xu <peterx@redhat.com>
|
|
(cherry picked from commit b84f06c2bee727b3870b4eeccbe3a45c5aea14c1)
|
|
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
|
|
Signed-off-by: zhujun2 <zhujun2_yewu@cmss.chinamobile.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 2c8b83f811..87f49e70c1 100644
|
|
--- a/system/physmem.c
|
|
+++ b/system/physmem.c
|
|
@@ -1531,18 +1531,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;
|
|
@@ -1795,13 +1783,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 */
|
|
@@ -1833,6 +1819,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)
|
|
@@ -1841,11 +1829,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);
|
|
|
|
@@ -1873,11 +1859,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.
|
|
--
|
|
2.41.0.windows.1
|
|
|