qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH V2 0/2] linux-user: add a syscall-filter plugin API
@ 2025-10-22  6:52 Ziyang Zhang
  2025-10-22  6:53 ` [RFC PATCH V2 1/2] linux-user: add a plugin API to filter syscalls Ziyang Zhang
  2025-10-22  6:54 ` [RFC PATCH V2 2/2] tcg tests: add a test to verify the syscall filter plugin API Ziyang Zhang
  0 siblings, 2 replies; 7+ messages in thread
From: Ziyang Zhang @ 2025-10-22  6:52 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

Hi,

This commit introduces a syscall filtering mechanism for user-mode
emulation, allowing plugins to intercept and handle system calls.

The filtering mechanism works by allowing the plugins to register a
callback that is invoked before each system call. The callback can decide
whether to filter and skip the current syscall.

The syscall arguments will be dispatched in sequence to all the plugins
that have registered the filter callback. If a plugin returns true,
indicating that it has filtered this syscall, it needs to set the sysret.
After that, the syscall will no longer be dispatched to the rest plugins
and will also not be dispatched to the default handler (i.e. do_syscall1)
of QEMU.

The original syscall tracing will not be affected. If syscall is filtered,
then the callbacks used to track sysret will be recorded to the filtered
sysret.

Key changes from RFC V1 [1] to V2:
- Simplify the syscall filter mechanism and remove the fork-cpu-loop part,
  which may be implemented in the future due to its complexity.
- Reuse the existing linked list registration utility, following the
  registration style of the original syscall tracing APIs.
- Add the QEMU_PLUGIN_EV_VCPU_SYSCALL_FILTER enum to qemu_plugin_event.
- Introduce a new plugin API: qemu_plugin_register_vcpu_syscall_filter_cb.

Though Florian's set_pc method [2] offers greater flexibility, I believe
that the security and reliability guaranteed by this event filtering
approach is more important.

## Work Presented at KVM Forum Conference

At KVM Forum 2025, we presented Lorelei, a system designed to enable
guest programs executed by the QEMU user-mode emulator to accelerate
performance by invoking natively compiled host libraries via syscalls.
We have successfully supported libraries such as SDL, OpenGL, and
Vulkan, allowing QEMU user-mode to emulate GPU-dependent games.

Following an invitation from Mr. Bouvier to upstream Lorelei to QEMU, we
refined its architecture to load the Lorelei module as a TCG plugin.

Slides: https://gitlab.com/qemu-project/kvm-forum/-/raw/main/_attachments/2025/Slides_DQPMaZE.pdf
Video: https://www.youtube.com/watch?v=_jioQFm7wyU&list=PLW3ep1uCIRfxwmllXTOA2txfDWN6vUOHp&index=22

The core features required by Lorelei are the filtering processing of
system calls and the reentry and exit of CPU loops. We can start talking
about syscall filtering first.

This site [3] shows the currently supported libraries and games by
Lorelei-patched qemu-x86_64. The original qemu-x86_64 cannot leverage the
host hardware accelerators, thus fails to run these games.

We will gradually update this site, including adding the usage of
qemu-lorelei, allowing users to run games and other GUI programs using
QEMU by themselves.

[1] https://lore.kernel.org/qemu-devel/625873322.3277896.1759930752814.JavaMail.zimbra@sjtu.edu.cn/
[2] https://lists.nongnu.org/archive/html/qemu-devel/2025-08/msg00656.html
[3] https://lorelei-project.github.io/

Regards,
Ziyang Zhang

Signed-off-by: Ziyang Zhang <functioner@sjtu.edu.cn>
---
Ziyang Zhang (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                            | 28 +++++++++++++------
 include/qemu/qemu-plugin.h                       | 24 +++++++++++++++++
 include/user/syscall-trace.h                     | 16 +++++++++++
 linux-user/syscall.c                             |  7 +++--
 plugins/api.c                                    |  7 +++++
 plugins/core.c                                   | 34 ++++++++++++++++++++++++
 tests/tcg/multiarch/Makefile.target              |  4 ++-
 tests/tcg/multiarch/test-plugin-syscall-filter.c | 21 +++++++++++++++
 tests/tcg/plugins/syscall.c                      | 14 ++++++++++
 10 files changed, 145 insertions(+), 11 deletions(-)
---
base-commit: 37ad0e48e9fd58b170abbf31c18a994346f62ed7
change-id: 20251022-lorelei-rfc-b4-03297a039dae

Best regards,
-- 
Ziyang Zhang <functioner@sjtu.edu.cn>


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

* [RFC PATCH V2 1/2] linux-user: add a plugin API to filter syscalls
  2025-10-22  6:52 [RFC PATCH V2 0/2] linux-user: add a syscall-filter plugin API Ziyang Zhang
@ 2025-10-22  6:53 ` Ziyang Zhang
  2025-10-22 16:00   ` Richard Henderson
  2025-10-22  6:54 ` [RFC PATCH V2 2/2] tcg tests: add a test to verify the syscall filter plugin API Ziyang Zhang
  1 sibling, 1 reply; 7+ messages in thread
From: Ziyang Zhang @ 2025-10-22  6:53 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

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>
---
 include/qemu/plugin-event.h  |  1 +
 include/qemu/plugin.h        | 28 ++++++++++++++++++++--------
 include/qemu/qemu-plugin.h   | 24 ++++++++++++++++++++++++
 include/user/syscall-trace.h | 16 ++++++++++++++++
 linux-user/syscall.c         |  7 +++++--
 plugins/api.c                |  7 +++++++
 plugins/core.c               | 34 ++++++++++++++++++++++++++++++++++
 7 files changed, 107 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..adba2ac49c 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,10 @@ 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 +272,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..719b070e32 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..34f7d6c7d4 100644
--- a/include/user/syscall-trace.h
+++ b/include/user/syscall-trace.h
@@ -39,5 +39,21 @@ static inline void record_syscall_return(CPUState *cpu, int num, abi_long ret)
     gdb_syscall_return(cpu, num);
 }
 
+static inline 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..b8225f838f 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..1b2f875fb1 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -538,6 +538,40 @@ 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);
+            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.25.1



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

* [RFC PATCH V2 2/2] tcg tests: add a test to verify the syscall filter plugin API
  2025-10-22  6:52 [RFC PATCH V2 0/2] linux-user: add a syscall-filter plugin API Ziyang Zhang
  2025-10-22  6:53 ` [RFC PATCH V2 1/2] linux-user: add a plugin API to filter syscalls Ziyang Zhang
@ 2025-10-22  6:54 ` Ziyang Zhang
  1 sibling, 0 replies; 7+ messages in thread
From: Ziyang Zhang @ 2025-10-22  6:54 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

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>
---
 tests/tcg/multiarch/Makefile.target              |  4 +++-
 tests/tcg/multiarch/test-plugin-syscall-filter.c | 21 +++++++++++++++++++++
 tests/tcg/plugins/syscall.c                      | 14 ++++++++++++++
 3 files changed, 38 insertions(+), 1 deletion(-)

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..e8676e4c29
--- /dev/null
+++ b/tests/tcg/multiarch/test-plugin-syscall-filter.c
@@ -0,0 +1,21 @@
+/*
+ * 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;
+}
diff --git a/tests/tcg/plugins/syscall.c b/tests/tcg/plugins/syscall.c
index 42801f5c86..310f69ff05 100644
--- a/tests/tcg/plugins/syscall.c
+++ b/tests/tcg/plugins/syscall.c
@@ -170,6 +170,19 @@ 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 +268,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.25.1


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

* Re: [RFC PATCH V2 1/2] linux-user: add a plugin API to filter syscalls
  2025-10-22  6:53 ` [RFC PATCH V2 1/2] linux-user: add a plugin API to filter syscalls Ziyang Zhang
@ 2025-10-22 16:00   ` Richard Henderson
  2025-10-27  7:14     ` Pierrick Bouvier
  0 siblings, 1 reply; 7+ messages in thread
From: Richard Henderson @ 2025-10-22 16:00 UTC (permalink / raw)
  To: Ziyang Zhang, qemu-devel
  Cc: Riku Voipio, Laurent Vivier, alex bennee, Alexandre Iooss,
	Mahmoud Mandour, Pierrick Bouvier, Zhengwei Qi, Yun Wang,
	Mingyuan Xia, Kailiang Xu

On 10/22/25 01:53, Ziyang Zhang wrote:
> @@ -165,6 +166,10 @@ 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);

The second and third lines should indented just past the ( on the first line, i.e. with 
CPUState.

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

Like this.

> +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);

Likewise.

> +static inline 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)

Do not mark inline; let the compiler decide.

> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index d78b2029fa..b8225f838f 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)) {

Incorrect indent.

> +        ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4,
> +                        arg5, arg6, arg7, arg8);

Likewise.

> diff --git a/plugins/core.c b/plugins/core.c
> index ead09fd2f1..1b2f875fb1 100644
> --- a/plugins/core.c
> +++ b/plugins/core.c
> @@ -538,6 +538,40 @@ 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)

Likewise.

> +{
> +    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);
> +            break;
> +        }
> +        qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
> +    }
> +    return filtered;
> +}

The loop is better written

     QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
         bool filtered;

         qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
         filtered = cb->f.vcpu_syscall_filter(cb->ctx->id, cpu->cpu_index,
                                              num, a1, a2, a3, a4, a5,
                                              a6, a7, a8, ret);
         qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);

         if (filtered) {
             return true;
         }
     }
     return false;


r~


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

* Re: [RFC PATCH V2 1/2] linux-user: add a plugin API to filter syscalls
  2025-10-22 16:00   ` Richard Henderson
@ 2025-10-27  7:14     ` Pierrick Bouvier
  2025-10-28 11:17       ` Richard Henderson
  0 siblings, 1 reply; 7+ messages in thread
From: Pierrick Bouvier @ 2025-10-27  7:14 UTC (permalink / raw)
  To: Richard Henderson, Ziyang Zhang, qemu-devel
  Cc: Riku Voipio, Laurent Vivier, alex bennee, Alexandre Iooss,
	Mahmoud Mandour, Zhengwei Qi, Yun Wang, Mingyuan Xia, Kailiang Xu

Hi Richard and Alex,

On 2025-10-22 18:00, Richard Henderson wrote:
> On 10/22/25 01:53, Ziyang Zhang wrote:
>> @@ -165,6 +166,10 @@ 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);
> 
> The second and third lines should indented just past the ( on the first line, i.e. with
> CPUState.
> 
>> +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)
> 
> Like this.
> 
>> +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);
> 
> Likewise.
> 
>> +static inline 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)
> 
> Do not mark inline; let the compiler decide.
> 
>> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
>> index d78b2029fa..b8225f838f 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)) {
> 
> Incorrect indent.
> 
>> +        ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4,
>> +                        arg5, arg6, arg7, arg8);
> 
> Likewise.
> 
>> diff --git a/plugins/core.c b/plugins/core.c
>> index ead09fd2f1..1b2f875fb1 100644
>> --- a/plugins/core.c
>> +++ b/plugins/core.c
>> @@ -538,6 +538,40 @@ 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)
> 
> Likewise.
> 
>> +{
>> +    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);
>> +            break;
>> +        }
>> +        qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
>> +    }
>> +    return filtered;
>> +}
> 
> The loop is better written
> 
>       QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
>           bool filtered;
> 
>           qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
>           filtered = cb->f.vcpu_syscall_filter(cb->ctx->id, cpu->cpu_index,
>                                                num, a1, a2, a3, a4, a5,
>                                                a6, a7, a8, ret);
>           qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
> 
>           if (filtered) {
>               return true;
>           }
>       }
>       return false;
> 
> 
> r~

Thanks for the review.
Beyond the code style, are you open to accept such a functionality as 
part of API plugins?

I think it's simple and powerful enough to justify having it upstream. 
Plus, it will solve other issues than integration with Lorelei.

Regards,
Pierrick


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

* Re: [RFC PATCH V2 1/2] linux-user: add a plugin API to filter syscalls
  2025-10-27  7:14     ` Pierrick Bouvier
@ 2025-10-28 11:17       ` Richard Henderson
  2025-10-30 13:29         ` Ziyang Zhang
  0 siblings, 1 reply; 7+ messages in thread
From: Richard Henderson @ 2025-10-28 11:17 UTC (permalink / raw)
  To: Pierrick Bouvier, Ziyang Zhang, qemu-devel
  Cc: Riku Voipio, Laurent Vivier, alex bennee, Alexandre Iooss,
	Mahmoud Mandour, Zhengwei Qi, Yun Wang, Mingyuan Xia, Kailiang Xu

On 10/27/25 08:14, Pierrick Bouvier wrote:
> 
> Thanks for the review.
> Beyond the code style, are you open to accept such a functionality as part of API plugins?

Yes.


r~


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

* Re: [RFC PATCH V2 1/2] linux-user: add a plugin API to filter syscalls
  2025-10-28 11:17       ` Richard Henderson
@ 2025-10-30 13:29         ` Ziyang Zhang
  0 siblings, 0 replies; 7+ messages in thread
From: Ziyang Zhang @ 2025-10-30 13:29 UTC (permalink / raw)
  To: richard henderson
  Cc: Pierrick Bouvier, qemu-devel, Riku Voipio, Laurent Vivier,
	alex bennee, Alexandre Iooss, Mahmoud Mandour, Zhengwei Qi,
	Yun Wang, Mingyuan Xia, Kailiang Xu

On 10/28/25 19:17, Richard Henderson wrote:
>>
>> Thanks for the review.
>> Beyond the code style, are you open to accept such a functionality as part of API plugins?
>
> Yes.

Thank you for your agreement!

Considering that our ultimate goal is to enable QEMU to support all features
of Lorelei, the current syscall filter can only play a limited role.

Here are some features currently required by Lorelei:

1. We need to bypass checks such as `pageflags` in `accel/tcg/user-exec.c`.
This is because the host library may provide memory blocks to the guest
program, and these memory blocks cannot be recorded by the page tables
emulated by `qemu-user`. As a result, errors may occur when invoking syscalls
like `read/write`.

2. The `cpu_loop` should support recursive invocation and exit. This is
necessary when the host library needs to execute guest callbacks, and the
recursively invoked `cpu_loop` should be able to return after the
callback finishes.

3. Since the host library may create new threads and execute guest callbacks
within those threads, the `thread_cpu` is `NULL` when the callback is first
executed. Therefore, a mechanism is required to create a `CPUState` when
`thread_cpu` is NULL.

Do you have any suggestions about how to implement these features?


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

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

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-22  6:52 [RFC PATCH V2 0/2] linux-user: add a syscall-filter plugin API Ziyang Zhang
2025-10-22  6:53 ` [RFC PATCH V2 1/2] linux-user: add a plugin API to filter syscalls Ziyang Zhang
2025-10-22 16:00   ` Richard Henderson
2025-10-27  7:14     ` Pierrick Bouvier
2025-10-28 11:17       ` Richard Henderson
2025-10-30 13:29         ` Ziyang Zhang
2025-10-22  6:54 ` [RFC PATCH V2 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).