Commit 46a8b8b378 for qemu.org
commit 46a8b8b378259a4ddb4bc4b79f88eba7640ad899
Author: y zhou <yzhou.dev@outlook.com>
Date: Mon Oct 5 11:18:17 2026 -0700
plugins: fix register handle encoding to reserve NULL as invalid
The opaque register handles returned by qemu_plugin_get_registers()
are currently encoded as:
handle = (gdb_reg << 1) | read_only_bit
For the first core register of every target -- gdb_reg 0, not
read-only -- this encodes to GINT_TO_POINTER(0), i.e. NULL. That is
x86_64 "rax", aarch64 "x0" and ppc "r0", among others.
A NULL handle is indistinguishable from an invalid one, so any plugin
that defensively checks its handles silently loses access to the
first core register (where the Linux syscall number lives on x86_64,
for example). Passing NULL to qemu_plugin_write_register() instead of
being rejected silently writes register 0.
Restore the invariant that NULL is never a valid handle by keeping an
offset on the gdb register number, as was already done before the
read-only property was packed into the handle (see v10.0.0, where
handles were encoded as GINT_TO_POINTER(gdb_reg + 1)):
handle = ((gdb_reg + 1) << 1) | read_only_bit
Also assert in tests/tcg/plugins/registers.c that every descriptor
carries a non-NULL handle: the current test passes the handles
straight to read/write without checking them, and a handle encoding
that maps a valid register to NULL goes unnoticed for that reason.
Found while writing a system-mode syscall tracing plugin, which could
read neither the syscall number on x86_64 nor the first argument on
aarch64 (both handles came back NULL). Auditing every
GINT_TO_POINTER/GPOINTER_TO_INT site in the tree shows this is the
only place where a valid entity encodes to NULL.
Fixes: 55327b85ce32c ("plugins: prohibit writing to read-only registers")
Signed-off-by: y zhou <yzhou.dev@outlook.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Cc: Alexandre Iooss <erdnaxe@crans.org>
Cc: qemu-stable@nongnu.org
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Link: https://lore.kernel.org/qemu-devel/20261005181817.403766-1-pierrick.bouvier@oss.qualcomm.com
Signed-off-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
diff --git a/plugins/api.c b/plugins/api.c
index 3520c96f41..46cba6796c 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -455,7 +455,7 @@ static GArray *create_register_handles(GArray *gdbstub_regs)
desc.is_readonly = true;
plugin_ro_bit = 1;
}
- desc.handle = GINT_TO_POINTER((grd->gdb_reg << 1) | plugin_ro_bit);
+ desc.handle = GINT_TO_POINTER(((grd->gdb_reg + 1) << 1) | plugin_ro_bit);
desc.feature = g_intern_string(grd->feature_name);
g_array_append_val(find_data, desc);
}
@@ -480,7 +480,7 @@ bool qemu_plugin_read_register(struct qemu_plugin_register *reg,
return false;
}
- return (gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) >> 1) > 0);
+ return (gdb_read_register(current_cpu, buf, (GPOINTER_TO_INT(reg) >> 1) - 1) > 0);
}
bool qemu_plugin_write_register(struct qemu_plugin_register *reg,
@@ -497,7 +497,8 @@ bool qemu_plugin_write_register(struct qemu_plugin_register *reg,
return false;
}
- return (gdb_write_register(current_cpu, buf->data, GPOINTER_TO_INT(reg) >> 1) > 0);
+ return (gdb_write_register(current_cpu, buf->data,
+ (GPOINTER_TO_INT(reg) >> 1) - 1) > 0);
}
void qemu_plugin_set_pc(uint64_t vaddr)
diff --git a/tests/tcg/plugins/registers.c b/tests/tcg/plugins/registers.c
index 0e41734435..02058fe8a8 100644
--- a/tests/tcg/plugins/registers.c
+++ b/tests/tcg/plugins/registers.c
@@ -29,6 +29,14 @@ static void vcpu_init_cb(unsigned int vcpu_index, void *userdata)
qemu_plugin_reg_descriptor *reg_desc = NULL;
bool success = false;
+ /* NULL must never be a valid register handle */
+ for (size_t i = 0; i < regs->len; i++) {
+ qemu_plugin_reg_descriptor *desc =
+ &g_array_index(regs, qemu_plugin_reg_descriptor, i);
+
+ g_assert(desc->handle != NULL);
+ }
+
/* Make sure we can read and write a register not marked as readonly */
for (size_t i = 0; i < regs->len; i++) {
reg_desc = &g_array_index(regs, qemu_plugin_reg_descriptor, i);