qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC 0/4] Support interactions between TCG plugins


From: Andrew Fasano
Subject: [RFC 0/4] Support interactions between TCG plugins
Date: Thu, 1 Sep 2022 14:27:30 -0400

Hello,

I'm requesting comments on the following series of patches expanding the
TCG plugin system to add the "QEMU Plugin-to-Plugin (QPP)" interface
that allows for interactions between TCG plugins. The goal of this
interface is to enable plugins to expand on other plugins and reduce
code duplication. This patch series includes documentation and
significant comments, but a high-level summary is below along with a
discussion of the current implementation as well as the benefits and
drawbacks of these changes.

**Summary**

The QPP interface allows two types of interactions between plugins:

1) Exported functions: A plugin may wish to allow other plugins to call
one of the functions it has defined. To do this, the plugin must mark
the function definition as publicly visible with the QEMU_PLUGIN_EXPORT
macro and place a definition in an included header file using the
QPP_FUN_PROTOTYPE macro. Other plugins can then include this header and
call the exported function by combining the name of the target plugin
with the name of the exported function.

For example, consider a hypothetical plugin that collects a list of
cache misses. This plugin could export two functions using the QPP
interface: one to allow another plugin to query this list and another
to empty the list. This would enable the development of another plugin
that examines guest CPU state to identify process changes and reports
the cache misses per process. With the QPP interface, this second plugin
would not need to duplicate any logic from the first.

2) Callbacks: Multiple plugins may wish to take some action when some
event of interest occurs inside a running guest. To support modularity
and reduce code duplication, the QPP callback system allows this logic
to be contained in single plugin that detects whenever a given event
occurs and exposes a callback with a given name. Another plugin can then
request to have one of its own functions run whenever this event occurs.
Additional plugins could also use this same callback to run additional
logic whenever this event occurs.

For example, consider (again) a hypothetical plugin that detects when
the current guest process changes by analyzing the guest CPU state. This
plugin could define a callback named "on_process_change" and trigger
this callback event whenever it detects a process change. Other plugins
could then be developed that take various actions on process changes by
registering internal functions to run on this event.

These patches and examples are inspired by the PANDA project
(https://panda.re and https://github.com/panda-re/panda), a fork of QEMU
modified to support dynamic program analysis and reverse engineering.
PANDA also includes a large plugin system with a similar interface for
interactions between plugins. I'm one of the maintainers of PANDA
and have seen how the ability for plugins to interact with
other plugins reduces code duplication and enables the creation of many
useful plugins.


**Implementation Overview**

These patches modify the TCG plugin build system to define the 
preprocessor variable CURRENT_PLUGIN to the name of the current plugin
based off its filename. This can be useful for plugin developers in
general and is used internally in the QPP implementation to determine
if an exported plugin function is defined in the current plugin or
in another.

These patches also add the function qemu_plugin_name_to_handle to the 
core plugin API which uses the new internal function is_plugin_named.
The ability for plugins to get a handle to another plugin is necessary
for the inter-plugin interactions described below.

The QPP implementation is contained inside a header file plugin-qpp.h
that adds the macros QPP_CREATE_CB, QPP_RUN_CB, QPP_REG_CB,
QPP_REMOVE_CB, and QPP_FUN_PROTOTYPE. The first 4 of these are related
to the callback system and the last one is for exported functions.

The QPP_CREATE_CB macro is used by a plugin that wishes to create a
callback with a given name. The macro will create an array of function
pointers for every function that has been registered to run on this
callback event. Other plugins can register a local function
with this callback (i.e., add it to this list of function pointers)
or remove a previously-registered function using QPP_REG_CB or
QPP_REMOVE_CB, respectively. When a plugin wishes to run all the
registered callback functions for a given callback, it uses the
QPP_RUN_CB macro.

The QPP_FUN_PROTOYPE macro enables a plugin to expose a function
it defines to other plugins. This macro is used in a header file that
is included by both the plugin that defines a function as well as
plugins that wish to use the function. This prototype creates a 
constructor function that runs on plugin load. If the target plugin
name differs from the value of CURRENT_PLUGIN, this function
will use qemu_plugin_name_to_handle to get a handle to the target
plugin and use g_module_symbol to resolve the target function in
that plugin. If this fails, it will print a warning and abort.

Finally, these patches include an example pair of plugins, qpp_srv and
qpp_client. The former provides a trivial QPP callback which simply runs
when the plugin is unloaded as well as two functions to add and subtract
integers. The latter of these plugins registers a function to run
on this callback and uses the two exported functions.


**Request for Comments**

This is my first time proposing any patches to QEMU and these are some
non-trivial changes, so I wanted to ask for feedback as opposed to just
sending these as a patch. I hope that others agree that there's value
in supporting interactions between plugins (particularly inter-plugin
callbacks), but I know there are many ways this could be implemented
and I'm sure there are ways this could be improved.

My goal in this design was to keep the process of creating and using
callbacks and exported functions as easy as possible for plugin
developers. I'm open to redesigning this interface in
a completely different way if that would be necessary to get it merged.

Although the example plugins I've included for now are trivial, if this
interface is merged I'd be willing to develop some more plugins that
build atop it.  One of these could be something like the "syscalls2"
plugin in PANDA which analyzes guest code and provides distinct
callbacks for when a guest runs a system call:
https://github.com/panda-re/panda/blob/dev/panda/plugins/syscalls2.
Another could be something like the "callstack_isntr" plugin in PANDA
which manages a shadow stack, provides callbacks on function calls and
returns and also provides exported functions to get the current
callstack:
https://github.com/panda-re/panda/tree/dev/panda/plugins/callstack_instr


I see a handful of potential issues that I'll highlight below:
1) Unstable APIs: these inter-plugin interactions do not specify API
   versions. If the behavior of a callback or exported function
   changes without the type signature changing, it may lead to subtle
   errors in other (unchanged) plugins that were using these functions.

   If the signature of a plugin's callback or exported function, 
   changes, build time errors would be raised and necessary
   changes could be made to in-tree plugins. However, out-of-tree
   plugins would break.

2) Checkpatch.pl objects to some of these changes. Most importantly,
   it takes issue with QPP_FUN_PROTOTYPE introducing multiple
   statements outside of a do while loop. I believe the logic in this
   macro cannot be wrapped in such a way as it is defining variables
   that must be available outside of the scope of the macro (in
   particular, these variables must be used by other QPP macros later).

   Given that this logic is contained in a plugin instead of the main
   QEMU binary, I'm hoping there's some leeway with this error, but
   if it needs to be changed, I could split the logic into 5 distinct
   macros that a plugin developer would then run in order. Or perhaps
   someone here can suggest a better design.

   Additional, I believe the script is raising a number of false
   positive errors for the indentation of the multi-line macros, e.g.,
   "suspect code indent for conditional statements (4, 6)" for
   QPP_RUN_CB. If I'm wrong about this, I'm happy to adjust the
   indentation. If it is an issue with the script, I could open a bug
   report if that would help.

3) Decentralized interactions. This approach allows plugins to directly
   interact with other plugins. An alternative design could register
   exported functions and callbacks with the core plugin logic in 
   the main QEMU object and have all the interactions go through there.
   Would a centralized design where plugins send requests through
   the core plugin logic in the QEMU binary be better that allowing
   direct interactions between the shared objects built for the plugins?

Does it seem like an interface like this would be worth merging? If so,
I'd welcome any and all suggestions on how to improve these changes.

Thank you,
Andrew Fasano

Andrew Fasano (4):
  docs/tcg-plugins: describe QPP API
  tcg/plugins: Automatically define CURRENT_PLUGIN
  tcg/plugins: Support for inter-plugin interactions
  tcg/plugins: Add example pair of QPP plugins

 contrib/plugins/Makefile     |   4 +-
 contrib/plugins/qpp_client.c |  42 ++++++
 contrib/plugins/qpp_client.h |   1 +
 contrib/plugins/qpp_srv.c    |  33 +++++
 contrib/plugins/qpp_srv.h    |  17 +++
 docs/devel/tcg-plugins.rst   |  76 +++++++++++
 include/qemu/plugin-qpp.h    | 252 +++++++++++++++++++++++++++++++++++
 plugins/core.c               |  11 ++
 plugins/loader.c             |  24 ++++
 plugins/plugin.h             |   5 +
 plugins/qemu-plugins.symbols |   1 +
 11 files changed, 465 insertions(+), 1 deletion(-)
 create mode 100644 contrib/plugins/qpp_client.c
 create mode 100644 contrib/plugins/qpp_client.h
 create mode 100644 contrib/plugins/qpp_srv.c
 create mode 100644 contrib/plugins/qpp_srv.h
 create mode 100644 include/qemu/plugin-qpp.h

-- 
2.34.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]