qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] linux-user: add a syscall-filter plugin API
@ 2025-11-10 13:34 Ziyang Zhang
  2025-11-10 13:34 ` [PATCH 1/2] linux-user: add a plugin API to filter syscalls Ziyang Zhang
  2025-11-10 13:34 ` [PATCH 2/2] tcg tests: add a test to verify the syscall filter plugin API Ziyang Zhang
  0 siblings, 2 replies; 3+ messages in thread
From: Ziyang Zhang @ 2025-11-10 13:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Riku Voipio, Laurent Vivier, Alex Bennee, Alexandre Iooss,
	Mahmoud Mandour, Pierrick Bouvier, Richard Henderson, Zhengwei Qi,
	Yun Wang, Mingyuan Xia, Kailiang Xu, Ziyang Zhang

We have improved the code format of RFC V2 [1] as suggested.

This patch modifies the indentation only.

RFC v2:
  1. Simplify the syscall filter mechanism and remove fork-cpu
  2. Add QEMU_PLUGIN_EV_VCPU_SYSCALL_FILTER enum

RFC v1:
  1. Add syscall filter registry
  2. Add fork-cpu interface to re-enter cpu_loop when handling syscalls

[1] https://lore.kernel.org/qemu-devel/1341969682.5812455.1761115945695.JavaMail.zimbra@sjtu.edu.cn/

Ziyang Zhang, Mingyuan Xia (2):
  linux-user: add a plugin API to filter syscalls
  tcg tests: add a test to verify the syscall filter plugin API

 include/qemu/plugin-event.h                   |  1 +
 include/qemu/plugin.h                         | 29 ++++++++++-----
 include/qemu/qemu-plugin.h                    | 24 +++++++++++++
 include/user/syscall-trace.h                  | 17 +++++++++
 linux-user/syscall.c                          |  7 ++--
 plugins/api.c                                 |  7 ++++
 plugins/core.c                                | 36 +++++++++++++++++++
 tests/tcg/multiarch/Makefile.target           |  4 ++-
 .../multiarch/test-plugin-syscall-filter.c    | 20 +++++++++++
 tests/tcg/plugins/syscall.c                   | 15 ++++++++
 10 files changed, 149 insertions(+), 11 deletions(-)
 create mode 100644 tests/tcg/multiarch/test-plugin-syscall-filter.c

-- 
2.34.1



^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] linux-user: add a plugin API to filter syscalls
  2025-11-10 13:34 [PATCH 0/2] linux-user: add a syscall-filter plugin API Ziyang Zhang
@ 2025-11-10 13:34 ` Ziyang Zhang
  2025-11-10 13:34 ` [PATCH 2/2] tcg tests: add a test to verify the syscall filter plugin API Ziyang Zhang
  1 sibling, 0 replies; 3+ messages in thread
From: Ziyang Zhang @ 2025-11-10 13:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Riku Voipio, Laurent Vivier, Alex Bennee, Alexandre Iooss,
	Mahmoud Mandour, Pierrick Bouvier, Richard Henderson, Zhengwei Qi,
	Yun Wang, Mingyuan Xia, Kailiang Xu, Ziyang Zhang

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        | 29 +++++++++++++++++++++--------
 include/qemu/qemu-plugin.h   | 24 ++++++++++++++++++++++++
 include/user/syscall-trace.h | 17 +++++++++++++++++
 linux-user/syscall.c         |  7 +++++--
 plugins/api.c                |  7 +++++++
 plugins/core.c               | 36 ++++++++++++++++++++++++++++++++++++
 7 files changed, 111 insertions(+), 10 deletions(-)

diff --git a/include/qemu/plugin-event.h b/include/qemu/plugin-event.h
index 7056d8427b..bbb1c2b91f 100644
--- a/include/qemu/plugin-event.h
+++ b/include/qemu/plugin-event.h
@@ -20,6 +20,7 @@ enum qemu_plugin_event {
     QEMU_PLUGIN_EV_VCPU_SYSCALL_RET,
     QEMU_PLUGIN_EV_FLUSH,
     QEMU_PLUGIN_EV_ATEXIT,
+    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 f355c7cb8a..9f90a233e7 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -55,14 +55,15 @@ 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_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_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;
 };
 
@@ -165,6 +166,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,
@@ -267,6 +273,13 @@ 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)
+{ }
+
 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 c450106af1..cdd0b2c4da 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -738,6 +738,25 @@ 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
+ * @vcpu_index: the executing vCPU
+ * @num: the syscall number
+ * @a1-a8: the syscall arguments
+ * @ret: the address of the syscall return value, set this if filtered
+ *
+ * Returns: true if you want to filter this syscall (i.e. stop it being
+ * handled further), otherwise return 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 *ret);
+
 QEMU_PLUGIN_API
 void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id,
                                           qemu_plugin_vcpu_syscall_cb_t cb);
@@ -751,6 +770,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..61cdbd7583 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 *ret)
+{
+    uint64_t sysret64 = 0;
+    bool filtered = qemu_plugin_vcpu_syscall_filter(cpu, num, arg1, arg2,
+                                                    arg3, arg4, arg5, arg6,
+                                                    arg7, arg8, &sysret64);
+    if (filtered) {
+        *ret = sysret64;
+    }
+    return filtered;
+}
+
 
 #endif /* SYSCALL_TRACE_H */
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index d78b2029fa..1fb99dcf0c 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -14084,8 +14084,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 ead09fd2f1..8cd773cbca 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -538,6 +538,42 @@ 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 *ret)
+{
+    struct qemu_plugin_cb *cb, *next;
+    enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL_FILTER;
+
+    if (!test_bit(ev, cpu->plugin_state->event_mask)) {
+        return false;
+    }
+
+    bool filtered = false;
+    QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
+        qemu_plugin_vcpu_syscall_filter_cb_t func = cb->f.vcpu_syscall_filter;
+
+        qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
+        if (func(cb->ctx->id, cpu->cpu_index, num, a1, a2, a3, a4,
+                 a5, a6, a7, a8, ret)) {
+            filtered = true;
+        }
+        qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
+
+        if (filtered)
+            break;
+    }
+    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



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] tcg tests: add a test to verify the syscall filter plugin API
  2025-11-10 13:34 [PATCH 0/2] linux-user: add a syscall-filter plugin API Ziyang Zhang
  2025-11-10 13:34 ` [PATCH 1/2] linux-user: add a plugin API to filter syscalls Ziyang Zhang
@ 2025-11-10 13:34 ` Ziyang Zhang
  1 sibling, 0 replies; 3+ messages in thread
From: Ziyang Zhang @ 2025-11-10 13:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: Riku Voipio, Laurent Vivier, Alex Bennee, Alexandre Iooss,
	Mahmoud Mandour, Pierrick Bouvier, Richard Henderson, Zhengwei Qi,
	Yun Wang, Mingyuan Xia, Kailiang Xu, Ziyang Zhang

Register a syscall filter callback in tests/tcg/plugins/sycall.c,
returns a specific value for a magic system call number, and check
it in tests/tcg/multiarch/test-plugin-syscall-filter.c.

Signed-off-by: Ziyang Zhang <functioner@sjtu.edu.cn>
Co-authored-by: Mingyuan Xia <xiamy@ultrarisc.com>
---
 tests/tcg/multiarch/Makefile.target           |  4 +++-
 .../multiarch/test-plugin-syscall-filter.c    | 20 +++++++++++++++++++
 tests/tcg/plugins/syscall.c                   | 15 ++++++++++++++
 3 files changed, 38 insertions(+), 1 deletion(-)
 create mode 100644 tests/tcg/multiarch/test-plugin-syscall-filter.c

diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target
index f5b4d2b813..4005e3a8a9 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -202,8 +202,10 @@ run-plugin-test-plugin-mem-access-with-libmem.so: \
 	CHECK_PLUGIN_OUTPUT_COMMAND= \
 	$(SRC_PATH)/tests/tcg/multiarch/check-plugin-output.sh \
 	$(QEMU) $<
+run-plugin-test-plugin-syscall-filter-with-libsyscall.so:
 
-EXTRA_RUNS_WITH_PLUGIN += run-plugin-test-plugin-mem-access-with-libmem.so
+EXTRA_RUNS_WITH_PLUGIN += run-plugin-test-plugin-mem-access-with-libmem.so \
+			   			  run-plugin-test-plugin-syscall-filter-with-libsyscall.so
 endif
 
 # Update TESTS
diff --git a/tests/tcg/multiarch/test-plugin-syscall-filter.c b/tests/tcg/multiarch/test-plugin-syscall-filter.c
new file mode 100644
index 0000000000..cc694e0a71
--- /dev/null
+++ b/tests/tcg/multiarch/test-plugin-syscall-filter.c
@@ -0,0 +1,20 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This test attempts to execute a magic syscall. The syscall test plugin
+ * should intercept this and returns an expected value.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main(int argc, char *argv[]) {
+    long ret = syscall(0x66CCFF);
+    if (ret != 0xFFCC66) {
+        perror("ERROR: syscall returned unexpected value!!!");
+        return EXIT_FAILURE;
+    }
+    return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/tests/tcg/plugins/syscall.c b/tests/tcg/plugins/syscall.c
index 42801f5c86..1323e18bc0 100644
--- a/tests/tcg/plugins/syscall.c
+++ b/tests/tcg/plugins/syscall.c
@@ -170,6 +170,20 @@ static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx,
     }
 }
 
+static bool vcpu_syscall_filter(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 *ret)
+{
+    if (num == 0x66CCFF) {
+        *ret = 0xFFCC66;
+        qemu_plugin_outs("syscall 0x66CCFF filtered, ret=0xFFCC66\n");
+        return true;
+    }
+    return false;
+}
+
 static void print_entry(gpointer val, gpointer user_data)
 {
     SyscallStats *entry = (SyscallStats *) val;
@@ -255,6 +269,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
 
     qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall);
     qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret);
+    qemu_plugin_register_vcpu_syscall_filter_cb(id, vcpu_syscall_filter);
     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
     return 0;
 }
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-11-10 13:53 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-10 13:34 [PATCH 0/2] linux-user: add a syscall-filter plugin API Ziyang Zhang
2025-11-10 13:34 ` [PATCH 1/2] linux-user: add a plugin API to filter syscalls Ziyang Zhang
2025-11-10 13:34 ` [PATCH 2/2] tcg tests: add a test to verify the syscall filter plugin API Ziyang Zhang

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).