Commit f7594e0ced for qemu.org
commit f7594e0cedb348169521933b00966c78cb7ba2c6
Author: TANG Tiancheng <lyndra@linux.alibaba.com>
Date: Mon Aug 31 15:40:35 2026 +0800
target/riscv/tcg: fix user option hash table leaks
The misa_ext_user_opts and multi_ext_user_opts hash tables are global,
but riscv_tcg_cpu_instance_init() recreates both for every CPU instance.
Creating a second CPU overwrites the only references to the first CPU's
tables. LeakSanitizer reports two direct and four indirect leaks, 320
bytes in total, when a two-hart virt machine exits.
The option state belongs to a CPU instance, like user_options. Move both
tables into RISCVCPU, pass the CPU to their access helpers, and free them
from the existing instance finalizer. Keep the implied-rule tables global
because those are initialized once and shared.
Fixes: 549cbf789ef8 ("target/riscv/cpu.c: introduce RISCVCPUMultiExtConfig")
Fixes: 21915d16c6fb ("target/riscv/tcg: add MISA user options hash")
Signed-off-by: TANG Tiancheng <lyndra@linux.alibaba.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20260831-b4-riscv-cpu-user-option-leak-v1-1-6b020c8b6964@linux.alibaba.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index a760ba3a52..652311ddef 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -3279,6 +3279,8 @@ static void riscv_cpu_instance_finalize(Object *obj)
g_clear_pointer(&cpu->pmu_event_ctr_map, g_hash_table_destroy);
#endif
g_clear_pointer(&cpu->user_options, g_hash_table_destroy);
+ g_clear_pointer(&cpu->misa_ext_user_opts, g_hash_table_destroy);
+ g_clear_pointer(&cpu->multi_ext_user_opts, g_hash_table_destroy);
}
static const TypeInfo riscv_cpu_type_infos[] = {
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index b974c2f4b4..c2138dbd4b 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -582,6 +582,8 @@ struct ArchCPU {
/* Mapping of events to counters */
GHashTable *pmu_event_ctr_map;
GHashTable *user_options;
+ GHashTable *misa_ext_user_opts;
+ GHashTable *multi_ext_user_opts;
const GPtrArray *decoders;
};
diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c
index 54f1b66216..b68160af83 100644
--- a/target/riscv/tcg/tcg-cpu.c
+++ b/target/riscv/tcg/tcg-cpu.c
@@ -41,34 +41,32 @@
#include "target/riscv/tcg/csr.h"
#endif
-/* Hash that stores user set extensions */
-static GHashTable *multi_ext_user_opts;
-static GHashTable *misa_ext_user_opts;
-
static GHashTable *multi_ext_implied_rules;
static GHashTable *misa_ext_implied_rules;
-static bool cpu_cfg_ext_is_user_set(uint32_t ext_offset)
+static bool cpu_cfg_ext_is_user_set(RISCVCPU *cpu, uint32_t ext_offset)
{
- return g_hash_table_contains(multi_ext_user_opts,
+ return g_hash_table_contains(cpu->multi_ext_user_opts,
GUINT_TO_POINTER(ext_offset));
}
-static bool cpu_misa_ext_is_user_set(uint32_t misa_bit)
+static bool cpu_misa_ext_is_user_set(RISCVCPU *cpu, uint32_t misa_bit)
{
- return g_hash_table_contains(misa_ext_user_opts,
+ return g_hash_table_contains(cpu->misa_ext_user_opts,
GUINT_TO_POINTER(misa_bit));
}
-static void cpu_cfg_ext_add_user_opt(uint32_t ext_offset, bool value)
+static void cpu_cfg_ext_add_user_opt(RISCVCPU *cpu, uint32_t ext_offset,
+ bool value)
{
- g_hash_table_insert(multi_ext_user_opts, GUINT_TO_POINTER(ext_offset),
+ g_hash_table_insert(cpu->multi_ext_user_opts,
+ GUINT_TO_POINTER(ext_offset),
(gpointer)value);
}
-static void cpu_misa_ext_add_user_opt(uint32_t bit, bool value)
+static void cpu_misa_ext_add_user_opt(RISCVCPU *cpu, uint32_t bit, bool value)
{
- g_hash_table_insert(misa_ext_user_opts, GUINT_TO_POINTER(bit),
+ g_hash_table_insert(cpu->misa_ext_user_opts, GUINT_TO_POINTER(bit),
(gpointer)value);
}
@@ -358,7 +356,7 @@ static void cpu_cfg_ext_auto_update(RISCVCPU *cpu, uint32_t ext_offset,
return;
}
- if (cpu_cfg_ext_is_user_set(ext_offset)) {
+ if (cpu_cfg_ext_is_user_set(cpu, ext_offset)) {
return;
}
@@ -491,7 +489,7 @@ static void riscv_cpu_validate_g(RISCVCPU *cpu)
{
const char *warn_msg = "RVG mandates disabled extension %s";
uint32_t g_misa_bits[] = {RVI, RVM, RVA, RVF, RVD};
- bool send_warn = cpu_misa_ext_is_user_set(RVG);
+ bool send_warn = cpu_misa_ext_is_user_set(cpu, RVG);
for (int i = 0; i < ARRAY_SIZE(g_misa_bits); i++) {
uint32_t bit = g_misa_bits[i];
@@ -734,7 +732,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
}
if (cpu->cfg.ext_zicntr && !cpu->cfg.ext_zicsr) {
- if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zicntr))) {
+ if (cpu_cfg_ext_is_user_set(cpu, CPU_CFG_OFFSET(ext_zicntr))) {
error_setg(errp, "zicntr requires zicsr");
return;
}
@@ -742,7 +740,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
}
if (cpu->cfg.ext_zihpm && !cpu->cfg.ext_zicsr) {
- if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_zihpm))) {
+ if (cpu_cfg_ext_is_user_set(cpu, CPU_CFG_OFFSET(ext_zihpm))) {
error_setg(errp, "zihpm requires zicsr");
return;
}
@@ -802,8 +800,8 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
if ((cpu->cfg.ext_smctr || cpu->cfg.ext_ssctr) &&
(!riscv_has_ext(env, RVS) || !cpu->cfg.ext_sscsrind)) {
- if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_smctr)) ||
- cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_ssctr))) {
+ if (cpu_cfg_ext_is_user_set(cpu, CPU_CFG_OFFSET(ext_smctr)) ||
+ cpu_cfg_ext_is_user_set(cpu, CPU_CFG_OFFSET(ext_ssctr))) {
error_setg(errp, "Smctr and Ssctr require S-mode and Sscsrind");
return;
}
@@ -820,7 +818,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
#ifndef CONFIG_USER_ONLY
if (cpu->cfg.ext_svpbmt && cpu->cfg.max_satp_mode < VM_1_10_SV39) {
cpu->cfg.ext_svpbmt = false;
- if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_svpbmt))) {
+ if (cpu_cfg_ext_is_user_set(cpu, CPU_CFG_OFFSET(ext_svpbmt))) {
warn_report("svpbmt requires at least satp sv39, "
"current satp mode: %s",
satp_mode_str(cpu->cfg.max_satp_mode,
@@ -830,7 +828,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
if (cpu->cfg.ext_svnapot && cpu->cfg.max_satp_mode < VM_1_10_SV39) {
cpu->cfg.ext_svnapot = false;
- if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_svnapot))) {
+ if (cpu_cfg_ext_is_user_set(cpu, CPU_CFG_OFFSET(ext_svnapot))) {
warn_report("svnapot requires at least satp sv39, "
"current satp mode: %s",
satp_mode_str(cpu->cfg.max_satp_mode,
@@ -1007,7 +1005,7 @@ static void cpu_enable_implied_rule(RISCVCPU *cpu,
* If the user disabled the misa_bit do not re-enable it
* and do not apply any implied rules related to it.
*/
- if (cpu_misa_ext_is_user_set(misa_bits[i]) &&
+ if (cpu_misa_ext_is_user_set(cpu, misa_bits[i]) &&
!(env->misa_ext & misa_bits[i])) {
continue;
}
@@ -1143,7 +1141,7 @@ static void riscv_cpu_update_misa_c(RISCVCPU *cpu)
}
if (set_misa_c) {
- if (cpu_misa_ext_is_user_set(RVC)) {
+ if (cpu_misa_ext_is_user_set(cpu, RVC)) {
warn_report("RVC mandated by Zca/Zcf/Zcd extensions");
return;
}
@@ -1285,7 +1283,7 @@ static void riscv_cpu_set_profile(RISCVCPU *cpu,
continue;
}
- cpu_misa_ext_add_user_opt(bit, profile->enabled);
+ cpu_misa_ext_add_user_opt(cpu, bit, profile->enabled);
riscv_cpu_write_misa_bit(cpu, bit, profile->enabled);
}
@@ -1296,7 +1294,7 @@ static void riscv_cpu_set_profile(RISCVCPU *cpu,
cpu_bump_multi_ext_priv_ver(&cpu->env, ext_offset);
}
- cpu_cfg_ext_add_user_opt(ext_offset, profile->enabled);
+ cpu_cfg_ext_add_user_opt(cpu, ext_offset, profile->enabled);
isa_ext_update_enabled(cpu, ext_offset, profile->enabled);
}
}
@@ -1356,7 +1354,7 @@ static void cpu_set_misa_ext_cfg(Object *obj, Visitor *v, const char *name,
return;
}
- cpu_misa_ext_add_user_opt(misa_bit, value);
+ cpu_misa_ext_add_user_opt(cpu, misa_bit, value);
prev_val = env->misa_ext & misa_bit;
@@ -1520,7 +1518,7 @@ static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name,
return;
}
- cpu_cfg_ext_add_user_opt(cfg_offset, value);
+ cpu_cfg_ext_add_user_opt(cpu, cfg_offset, value);
prev_val = isa_ext_is_enabled(cpu, cfg_offset);
@@ -1684,8 +1682,8 @@ static void riscv_tcg_cpu_instance_init(CPUState *cs)
"riscv.cpu.rnmi", RNMI_MAX);
#endif
- misa_ext_user_opts = g_hash_table_new(NULL, g_direct_equal);
- multi_ext_user_opts = g_hash_table_new(NULL, g_direct_equal);
+ cpu->misa_ext_user_opts = g_hash_table_new(NULL, g_direct_equal);
+ cpu->multi_ext_user_opts = g_hash_table_new(NULL, g_direct_equal);
if (!misa_ext_implied_rules) {
misa_ext_implied_rules = g_hash_table_new(NULL, g_direct_equal);