From: Ziyang Zhang <functioner@sjtu.edu.cn>
To: qemu-devel <qemu-devel@nongnu.org>
Cc: Riku Voipio <riku.voipio@iki.fi>,
Laurent Vivier <laurent@vivier.eu>,
Alex Bennee <alex.bennee@linaro.org>,
Alexandre Iooss <erdnaxe@crans.org>,
Mahmoud Mandour <ma.mandourr@gmail.com>,
Pierrick Bouvier <pierrick.bouvier@linaro.org>,
Richard Henderson <richard.henderson@linaro.org>,
Zhengwei Qi <qizhwei@sjtu.edu.cn>,
Yun Wang <yunwang94@sjtu.edu.cn>,
Mingyuan Xia <xiamy@ultrarisc.com>,
Kailiang Xu <xukl2019@sjtu.edu.cn>,
Ziyang Zhang <functioner@sjtu.edu.cn>
Subject: [PATCH v3 1/2] linux-user: add plugin API to filter syscalls
Date: Sun, 14 Dec 2025 22:10:21 +0800 [thread overview]
Message-ID: <20251214141022.173340-2-functioner@sjtu.edu.cn> (raw)
In-Reply-To: <20251214141022.173340-1-functioner@sjtu.edu.cn>
This commit adds a syscall filter API to the TCG plugin API set.
Plugins can register a filter callback to QEMU to decide whether
to intercept a syscall, process it and bypass the QEMU syscall
handler.
Signed-off-by: Ziyang Zhang <functioner@sjtu.edu.cn>
Co-authored-by: Mingyuan Xia <xiamy@ultrarisc.com>
---
include/qemu/plugin-event.h | 1 +
include/qemu/plugin.h | 33 +++++++++++++++++++++++---------
include/qemu/qemu-plugin.h | 32 +++++++++++++++++++++++++++++++
include/user/syscall-trace.h | 17 +++++++++++++++++
linux-user/syscall.c | 7 +++++--
plugins/api.c | 7 +++++++
plugins/core.c | 37 ++++++++++++++++++++++++++++++++++++
7 files changed, 123 insertions(+), 11 deletions(-)
diff --git a/include/qemu/plugin-event.h b/include/qemu/plugin-event.h
index 1100dae212..7f3c5927f1 100644
--- a/include/qemu/plugin-event.h
+++ b/include/qemu/plugin-event.h
@@ -23,6 +23,7 @@ enum qemu_plugin_event {
QEMU_PLUGIN_EV_VCPU_INTERRUPT,
QEMU_PLUGIN_EV_VCPU_EXCEPTION,
QEMU_PLUGIN_EV_VCPU_HOSTCALL,
+ QEMU_PLUGIN_EV_VCPU_SYSCALL_FILTER,
QEMU_PLUGIN_EV_MAX, /* total number of plugin events we support */
};
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index cea0a68858..91df1e78d2 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -55,15 +55,16 @@ void qemu_plugin_opt_parse(const char *optstr, QemuPluginList *head);
int qemu_plugin_load_list(QemuPluginList *head, Error **errp);
union qemu_plugin_cb_sig {
- qemu_plugin_simple_cb_t simple;
- qemu_plugin_udata_cb_t udata;
- qemu_plugin_vcpu_simple_cb_t vcpu_simple;
- qemu_plugin_vcpu_udata_cb_t vcpu_udata;
- qemu_plugin_vcpu_discon_cb_t vcpu_discon;
- qemu_plugin_vcpu_tb_trans_cb_t vcpu_tb_trans;
- qemu_plugin_vcpu_mem_cb_t vcpu_mem;
- qemu_plugin_vcpu_syscall_cb_t vcpu_syscall;
- qemu_plugin_vcpu_syscall_ret_cb_t vcpu_syscall_ret;
+ qemu_plugin_simple_cb_t simple;
+ qemu_plugin_udata_cb_t udata;
+ qemu_plugin_vcpu_simple_cb_t vcpu_simple;
+ qemu_plugin_vcpu_udata_cb_t vcpu_udata;
+ qemu_plugin_vcpu_discon_cb_t vcpu_discon;
+ qemu_plugin_vcpu_tb_trans_cb_t vcpu_tb_trans;
+ qemu_plugin_vcpu_mem_cb_t vcpu_mem;
+ qemu_plugin_vcpu_syscall_cb_t vcpu_syscall;
+ qemu_plugin_vcpu_syscall_ret_cb_t vcpu_syscall_ret;
+ qemu_plugin_vcpu_syscall_filter_cb_t vcpu_syscall_filter;
void *generic;
};
@@ -169,6 +170,11 @@ qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1,
uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5,
uint64_t a6, uint64_t a7, uint64_t a8);
void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret);
+bool
+qemu_plugin_vcpu_syscall_filter(CPUState *cpu, int64_t num, uint64_t a1,
+ uint64_t a2, uint64_t a3, uint64_t a4,
+ uint64_t a5, uint64_t a6, uint64_t a7,
+ uint64_t a8, uint64_t *ret);
void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
uint64_t value_low,
@@ -280,6 +286,15 @@ static inline
void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret)
{ }
+static inline bool
+qemu_plugin_vcpu_syscall_filter(CPUState *cpu, int64_t num, uint64_t a1,
+ uint64_t a2, uint64_t a3, uint64_t a4,
+ uint64_t a5, uint64_t a6, uint64_t a7,
+ uint64_t a8, uint64_t *ret)
+{
+ return false;
+}
+
static inline void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
uint64_t value_low,
uint64_t value_high,
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 60de4fdd3f..f3bff82257 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -798,6 +798,33 @@ typedef void
uint64_t a3, uint64_t a4, uint64_t a5,
uint64_t a6, uint64_t a7, uint64_t a8);
+/**
+ * typedef qemu_plugin_vcpu_syscall_filter_cb_t - vCPU syscall filter callback
+ * function type
+ * @id: plugin id
+ * @vcpu_index: the executing vCPU
+ * @num: the syscall number
+ * @a1: the 1st syscall argument
+ * @a2: the 2nd syscall argument
+ * @a3: the 3rd syscall argument
+ * @a4: the 4th syscall argument
+ * @a5: the 5th syscall argument
+ * @a6: the 6th syscall argument
+ * @a7: the 7th syscall argument
+ * @a8: the 8th syscall argument
+ * @sysret: the reference of the syscall return value, must set this if filtered
+ *
+ * Returns true if you want to filter this syscall (i.e. stop it being
+ * handled further), otherwise returns false.
+ */
+typedef bool
+(*qemu_plugin_vcpu_syscall_filter_cb_t)(qemu_plugin_id_t id,
+ unsigned int vcpu_index,
+ int64_t num, uint64_t a1, uint64_t a2,
+ uint64_t a3, uint64_t a4, uint64_t a5,
+ uint64_t a6, uint64_t a7, uint64_t a8,
+ uint64_t *sysret);
+
QEMU_PLUGIN_API
void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_syscall_cb_t cb);
@@ -811,6 +838,11 @@ void
qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id,
qemu_plugin_vcpu_syscall_ret_cb_t cb);
+QEMU_PLUGIN_API
+void
+qemu_plugin_register_vcpu_syscall_filter_cb(qemu_plugin_id_t id,
+ qemu_plugin_vcpu_syscall_filter_cb_t cb);
+
/**
* qemu_plugin_insn_disas() - return disassembly string for instruction
diff --git a/include/user/syscall-trace.h b/include/user/syscall-trace.h
index 9bd7ca19c8..4ee6c4e513 100644
--- a/include/user/syscall-trace.h
+++ b/include/user/syscall-trace.h
@@ -39,5 +39,22 @@ static inline void record_syscall_return(CPUState *cpu, int num, abi_long ret)
gdb_syscall_return(cpu, num);
}
+static bool send_through_syscall_filters(CPUState *cpu, int num,
+ abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4,
+ abi_long arg5, abi_long arg6,
+ abi_long arg7, abi_long arg8,
+ abi_long *sysret)
+{
+ uint64_t sysret64 = 0;
+ bool filtered = qemu_plugin_vcpu_syscall_filter(cpu, num, arg1, arg2,
+ arg3, arg4, arg5, arg6,
+ arg7, arg8, &sysret64);
+ if (filtered) {
+ *sysret = sysret64;
+ }
+ return filtered;
+}
+
#endif /* SYSCALL_TRACE_H */
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 2060e561a2..9ae3c5bfbd 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -14201,8 +14201,11 @@ abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1,
print_syscall(cpu_env, num, arg1, arg2, arg3, arg4, arg5, arg6);
}
- ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4,
- arg5, arg6, arg7, arg8);
+ if (!send_through_syscall_filters(cpu, num, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7, arg8, &ret)) {
+ ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7, arg8);
+ }
if (unlikely(qemu_loglevel_mask(LOG_STRACE))) {
print_syscall_ret(cpu_env, num, ret, arg1, arg2,
diff --git a/plugins/api.c b/plugins/api.c
index eac04cc1f6..478d0c8889 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -208,6 +208,13 @@ qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id,
plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, cb);
}
+void
+qemu_plugin_register_vcpu_syscall_filter_cb(qemu_plugin_id_t id,
+ qemu_plugin_vcpu_syscall_filter_cb_t cb)
+{
+ plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL_FILTER, cb);
+}
+
/*
* Plugin Queries
*
diff --git a/plugins/core.c b/plugins/core.c
index b4b783008f..85fabf9ec8 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -564,6 +564,43 @@ void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret)
}
}
+/*
+ * Disable CFI checks.
+ * The callback function has been loaded from an external library so we do not
+ * have type information
+ */
+QEMU_DISABLE_CFI
+bool
+qemu_plugin_vcpu_syscall_filter(CPUState *cpu, int64_t num, uint64_t a1,
+ uint64_t a2, uint64_t a3, uint64_t a4,
+ uint64_t a5, uint64_t a6, uint64_t a7,
+ uint64_t a8, uint64_t *sysret)
+{
+ struct qemu_plugin_cb *cb, *next;
+ enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL_FILTER;
+ bool filtered = false;
+
+ if (!test_bit(ev, cpu->plugin_state->event_mask)) {
+ return false;
+ }
+
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
+
+ QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
+ qemu_plugin_vcpu_syscall_filter_cb_t func = cb->f.vcpu_syscall_filter;
+
+ if (func(cb->ctx->id, cpu->cpu_index, num, a1, a2, a3, a4,
+ a5, a6, a7, a8, sysret)) {
+ filtered = true;
+ break;
+ }
+ }
+
+ qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
+
+ return filtered;
+}
+
void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
{
/* idle and resume cb may be called before init, ignore in this case */
--
2.34.1
next prev parent reply other threads:[~2025-12-14 14:12 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-14 14:10 [PATCH v3 0/2] linux-user: add plugin API to filter syscalls Ziyang Zhang
2025-12-14 14:10 ` Ziyang Zhang [this message]
2025-12-14 14:10 ` [PATCH v3 2/2] tcg tests: add a test to verify the syscall filter plugin API Ziyang Zhang
2025-12-14 14:26 ` [PATCH v3 0/2] linux-user: add plugin API to filter syscalls Ziyang Zhang
-- strict thread matches above, loose matches on Subject: below --
2025-12-14 14:19 Ziyang Zhang
2025-12-14 14:19 ` [PATCH v3 1/2] " Ziyang Zhang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251214141022.173340-2-functioner@sjtu.edu.cn \
--to=functioner@sjtu.edu.cn \
--cc=alex.bennee@linaro.org \
--cc=erdnaxe@crans.org \
--cc=laurent@vivier.eu \
--cc=ma.mandourr@gmail.com \
--cc=pierrick.bouvier@linaro.org \
--cc=qemu-devel@nongnu.org \
--cc=qizhwei@sjtu.edu.cn \
--cc=richard.henderson@linaro.org \
--cc=riku.voipio@iki.fi \
--cc=xiamy@ultrarisc.com \
--cc=xukl2019@sjtu.edu.cn \
--cc=yunwang94@sjtu.edu.cn \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).