qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Alex Bennée" <alex.bennee@linaro.org>
To: Ziyang Zhang <functioner@sjtu.edu.cn>
Cc: qemu-devel <qemu-devel@nongnu.org>,
	 Riku Voipio <riku.voipio@iki.fi>,
	Laurent Vivier <laurent@vivier.eu>,
	 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>
Subject: Re: [PATCH 1/2] linux-user: add a plugin API to filter syscalls
Date: Fri, 12 Dec 2025 14:29:09 +0000	[thread overview]
Message-ID: <87v7ibsrii.fsf@draig.linaro.org> (raw)
In-Reply-To: <20251110133442.579086-2-functioner@sjtu.edu.cn> (Ziyang Zhang's message of "Mon, 10 Nov 2025 21:34:42 +0800")

Ziyang Zhang <functioner@sjtu.edu.cn> writes:

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

This needs to return false so it compile and work with --disable-plugins.

>  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

"must set this..."

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

nit: keep args together in the block

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

I think setting these flags could be outside the loop.

> +        if (func(cb->ctx->id, cpu->cpu_index, num, a1, a2, a3, a4,
> +                 a5, a6, a7, a8, ret)) {
> +            filtered = true;

then you could just break here.

> +        }
> +        qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
> +
> +        if (filtered)
> +            break;

nit: brackets

> +    }
> +    return filtered;
> +}
> +
>  void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
>  {
>      /* idle and resume cb may be called before init, ignore in this case */

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


  parent reply	other threads:[~2025-12-12 14:29 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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-12-03  2:07   ` Pierrick Bouvier
2025-12-12 14:29   ` Alex Bennée [this message]
2025-11-10 13:34 ` [PATCH 2/2] tcg tests: add a test to verify the syscall filter plugin API Ziyang Zhang
2025-12-03  2:08   ` Pierrick Bouvier
2025-12-12 12:44 ` [PATCH 0/2] linux-user: add a syscall-filter " Alex Bennée

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=87v7ibsrii.fsf@draig.linaro.org \
    --to=alex.bennee@linaro.org \
    --cc=erdnaxe@crans.org \
    --cc=functioner@sjtu.edu.cn \
    --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).