Message ID | 1379066356-14986-6-git-send-email-famz@redhat.com |
---|---|
State | New |
Headers | show |
On Fri, Sep 13, 2013 at 05:59:13PM +0800, Fam Zheng wrote: > +void module_load(module_load_type type) > +{ > +#ifdef CONFIG_MODULES > + const char *path; > + char *fname = NULL; > + const char **mp; > + const char *module_whitelist[] = { > + CONFIG_MODULE_WHITELIST > + }; > + > + if (!g_module_supported()) { > + return; > + } > + > + switch (type) { > + case MODULE_LOAD_BLOCK: > + path = CONFIG_MODDIR "/block/"; > + break; > + case MODULE_LOAD_UI: > + path = CONFIG_MODDIR "/ui/"; > + break; > + case MODULE_LOAD_NET: > + path = CONFIG_MODDIR "/net/"; > + break; > + default: > + return; > + } > + > + for (mp = &module_whitelist[0]; *mp; mp++) { > + fname = g_strdup_printf("%s%s" HOST_DSOSUF, path, *mp); > + module_load_file(fname); > + g_free(fname); > + } You need a separate whitelist for block, ui, net, etc. This code just spews errors to stderr. Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/iscsi.so: cannot open shared object file: No such file or directory Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/curl.so: cannot open shared object file: No such file or directory Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/rbd.so: cannot open shared object file: No such file or directory Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/gluster.so: cannot open shared object file: No such file or directory Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/ssh.so: cannot open shared object file: No such file or directory Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/iscsi.so: cannot open shared object file: No such file or directory Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/curl.so: cannot open shared object file: No such file or directory Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/rbd.so: cannot open shared object file: No such file or directory Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/gluster.so: cannot open shared object file: No such file or directory Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/ssh.so: cannot open shared object file: No such file or directory was this even tested before being posted ? Daniel
On 09/13/2013 02:59 AM, Fam Zheng wrote: > + const char *module_whitelist[] = { > + CONFIG_MODULE_WHITELIST > + }; static const char * const module_whitelist[] = ... > + switch (type) { > + case MODULE_LOAD_BLOCK: > + path = CONFIG_MODDIR "/block/"; > + break; > + case MODULE_LOAD_UI: > + path = CONFIG_MODDIR "/ui/"; > + break; > + case MODULE_LOAD_NET: > + path = CONFIG_MODDIR "/net/"; > + break; Also, separate the whitelists by type. I.e. static const char * const modules_block[] = ... static const char * const modules_ui[] = ... static const char * const modules_net[] = ... switch (type) { case MODULE_LOAD_BLOCK: list = modules_block; n = ARRAY_SIZE(modules_block); break; ... } No need for null termination of the array, as you're currently using. > + for (mp = &module_whitelist[0]; *mp; mp++) { > + fname = g_strdup_printf("%s%s" HOST_DSOSUF, path, *mp); > + module_load_file(fname); > + g_free(fname); > + } Why this bizzare mix of g_strdup_printf and compile-time string concatenation? Certainly you could have arranged for HOST_DSOSUF to be built into the module name as seen in the arrays. Then we're back to the subdirectory vs filename prefix and CONFIG_MODDIR vs any of several module search path options, the debate of which I don't believe has concluded. r~
13.09.2013 18:52, Richard Henderson wrote:
> Also, separate the whitelists by type. I.e.
What for?
Thanks,
/mjt
On 09/13/2013 09:20 AM, Michael Tokarev wrote: > 13.09.2013 18:52, Richard Henderson wrote: > >> Also, separate the whitelists by type. I.e. > > What for? We are talking about a function whose interface is to only load modules of a particular type, right? Given that we know the set of all modules, and the type to which it belongs, how can it not be most efficient to separate them into different lists, to avoid having to perform some kind of runtime test on each module? r~
On Fri, 09/13 15:07, Daniel P. Berrange wrote: > On Fri, Sep 13, 2013 at 05:59:13PM +0800, Fam Zheng wrote: > > +void module_load(module_load_type type) > > +{ > > +#ifdef CONFIG_MODULES > > + const char *path; > > + char *fname = NULL; > > + const char **mp; > > + const char *module_whitelist[] = { > > + CONFIG_MODULE_WHITELIST > > + }; > > + > > + if (!g_module_supported()) { > > + return; > > + } > > + > > + switch (type) { > > + case MODULE_LOAD_BLOCK: > > + path = CONFIG_MODDIR "/block/"; > > + break; > > + case MODULE_LOAD_UI: > > + path = CONFIG_MODDIR "/ui/"; > > + break; > > + case MODULE_LOAD_NET: > > + path = CONFIG_MODDIR "/net/"; > > + break; > > + default: > > + return; > > + } > > + > > + for (mp = &module_whitelist[0]; *mp; mp++) { > > + fname = g_strdup_printf("%s%s" HOST_DSOSUF, path, *mp); > > + module_load_file(fname); > > + g_free(fname); > > + } > > You need a separate whitelist for block, ui, net, etc. This code just > spews errors to stderr. > > Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/iscsi.so: cannot open shared object file: No such file or directory > Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/curl.so: cannot open shared object file: No such file or directory > Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/rbd.so: cannot open shared object file: No such file or directory > Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/gluster.so: cannot open shared object file: No such file or directory > Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/ui/ssh.so: cannot open shared object file: No such file or directory > Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/iscsi.so: cannot open shared object file: No such file or directory > Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/curl.so: cannot open shared object file: No such file or directory > Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/rbd.so: cannot open shared object file: No such file or directory > Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/gluster.so: cannot open shared object file: No such file or directory > Failed to open module file /home/berrange/usr/qemu-git-mod/lib/qemu/net/ssh.so: cannot open shared object file: No such file or directory > Given that we should accept non-existence of a whitelisted module (i.e. user didn't install it), I think it's these -ENOENT error messges are unnecessary. Fam
On Fri, 09/13 07:52, Richard Henderson wrote: > On 09/13/2013 02:59 AM, Fam Zheng wrote: > > + const char *module_whitelist[] = { > > + CONFIG_MODULE_WHITELIST > > + }; > > static const char * const module_whitelist[] = ... > OK, thanks. > > + switch (type) { > > + case MODULE_LOAD_BLOCK: > > + path = CONFIG_MODDIR "/block/"; > > + break; > > + case MODULE_LOAD_UI: > > + path = CONFIG_MODDIR "/ui/"; > > + break; > > + case MODULE_LOAD_NET: > > + path = CONFIG_MODDIR "/net/"; > > + break; > > Also, separate the whitelists by type. I.e. > > static const char * const modules_block[] = ... > static const char * const modules_ui[] = ... > static const char * const modules_net[] = ... > > switch (type) { > case MODULE_LOAD_BLOCK: > list = modules_block; > n = ARRAY_SIZE(modules_block); > break; > ... > } > > No need for null termination of the array, as you're currently using. > It's that I'd like to be consistent with block whitelist code style as in bdrv_is_whitelisted(). > > + for (mp = &module_whitelist[0]; *mp; mp++) { > > + fname = g_strdup_printf("%s%s" HOST_DSOSUF, path, *mp); > > + module_load_file(fname); > > + g_free(fname); > > + } > > Why this bizzare mix of g_strdup_printf and compile-time string concatenation? > Certainly you could have arranged for HOST_DSOSUF to be built into the module > name as seen in the arrays. > > Then we're back to the subdirectory vs filename prefix and CONFIG_MODDIR vs any > of several module search path options, the debate of which I don't believe has > concluded. > Perhaps using module type as prefix and building into whitelist can save us creating subdirs for each type and also the separation of the list as you suggested above: switch (type) { case MODULE_LOAD_BLOCK: prefix = "block-"; break; ... } for (mp = &module_whitelist[0]; *mp; mp++) { if (!strncmp(prefix, *mp, sizeof(prefix))) { /* load */ } }
diff --git a/Makefile b/Makefile index ef76967..766b309 100644 --- a/Makefile +++ b/Makefile @@ -196,6 +196,9 @@ Makefile: $(version-obj-y) $(version-lobj-y) libqemustub.a: $(stub-obj-y) libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o +default-whitelist = $(foreach o,$(modules-m),"$(notdir $(basename $o))",) NULL +util/module.o-cflags = -D'CONFIG_MODULE_WHITELIST=$(default-whitelist)' + ###################################################################### qemu-img.o: qemu-img-cmds.h diff --git a/block.c b/block.c index 26639e8..16ceaaf 100644 --- a/block.c +++ b/block.c @@ -4008,6 +4008,7 @@ BlockDriverAIOCB *bdrv_aio_discard(BlockDriverState *bs, void bdrv_init(void) { + module_load(MODULE_LOAD_BLOCK); module_call_init(MODULE_INIT_BLOCK); } diff --git a/configure b/configure index 5bc7256..05aa964 100755 --- a/configure +++ b/configure @@ -199,6 +199,7 @@ datadir="\${prefix}/share" qemu_docdir="\${prefix}/share/doc/qemu" bindir="\${prefix}/bin" libdir="\${prefix}/lib" +moddir="\${prefix}/lib/qemu" libexecdir="\${prefix}/libexec" includedir="\${prefix}/include" sysconfdir="\${prefix}/etc" @@ -651,7 +652,9 @@ for opt do ;; --disable-debug-info) ;; - --enable-modules) modules="yes" + --enable-modules|--enable-modules=*) + modules="yes" + module_list=`echo "$optarg" | sed -e 's/,/ /g'` ;; --cpu=*) ;; @@ -676,6 +679,8 @@ for opt do ;; --libdir=*) libdir="$optarg" ;; + --moddir=*) moddir="$optarg" + ;; --libexecdir=*) libexecdir="$optarg" ;; --includedir=*) includedir="$optarg" @@ -1052,10 +1057,13 @@ echo " --datadir=PATH install firmware in PATH$confsuffix" echo " --docdir=PATH install documentation in PATH$confsuffix" echo " --bindir=PATH install binaries in PATH" echo " --libdir=PATH install libraries in PATH" +echo " --moddir=PATH install modules in PATH" echo " --sysconfdir=PATH install config in PATH$confsuffix" echo " --localstatedir=PATH install local state in PATH (set at runtime on win32)" echo " --with-confsuffix=SUFFIX suffix for QEMU data inside datadir and sysconfdir [$confsuffix]" -echo " --enable-modules enable modules support" +echo " --enable-modules enable modules support and whitelist all modules" +echo " --enable-modules=L enable modules and provide a whitelist" +echo " Available modules: curl iscsi gluster ssh rbd" echo " --enable-debug-tcg enable TCG debugging" echo " --disable-debug-tcg disable TCG debugging (default)" echo " --enable-debug-info enable debugging information (default)" @@ -2256,15 +2264,19 @@ if test "$mingw32" = yes; then else glib_req_ver=2.12 fi -if $pkg_config --atleast-version=$glib_req_ver gthread-2.0; then - glib_cflags=`$pkg_config --cflags gthread-2.0` - glib_libs=`$pkg_config --libs gthread-2.0` - CFLAGS="$glib_cflags $CFLAGS" - LIBS="$glib_libs $LIBS" - libs_qga="$glib_libs $libs_qga" -else - error_exit "glib-$glib_req_ver required to compile QEMU" -fi + +for i in gthread-2.0 gmodule-2.0; do + if $pkg_config --atleast-version=$glib_req_ver $i; then + glib_cflags=`$pkg_config --cflags $i` + glib_libs=`$pkg_config --libs $i` + CFLAGS="$glib_cflags $CFLAGS" + LIBS="$glib_libs $LIBS" + libs_qga="$glib_libs $libs_qga" + else + error_exit "glib-$glib_req_ver required to compile QEMU" + fi +done + ########################################## # pixman support probe @@ -3557,6 +3569,7 @@ echo "Install prefix $prefix" echo "BIOS directory `eval echo $qemu_datadir`" echo "binary directory `eval echo $bindir`" echo "library directory `eval echo $libdir`" +echo "module directory `eval echo $moddir`" echo "libexec directory `eval echo $libexecdir`" echo "include directory `eval echo $includedir`" echo "config directory `eval echo $sysconfdir`" @@ -3581,6 +3594,9 @@ if test "$slirp" = "yes" ; then echo "smbd $smbd" fi echo "module support $modules" +if test -n "$module_list"; then + echo "module whitelist $module_list" +fi echo "host CPU $cpu" echo "host big endian $bigendian" echo "target list $target_list" @@ -3680,6 +3696,7 @@ echo all: >> $config_host_mak echo "prefix=$prefix" >> $config_host_mak echo "bindir=$bindir" >> $config_host_mak echo "libdir=$libdir" >> $config_host_mak +echo "moddir=$moddir" >> $config_host_mak echo "libexecdir=$libexecdir" >> $config_host_mak echo "includedir=$includedir" >> $config_host_mak echo "mandir=$mandir" >> $config_host_mak @@ -3698,8 +3715,12 @@ echo "libs_softmmu=$libs_softmmu" >> $config_host_mak echo "ARCH=$ARCH" >> $config_host_mak +echo "CONFIG_FINGERPRINT=$(date +%s$$$RANDOM)" >> $config_host_mak if test "$modules" = "yes"; then echo "CONFIG_MODULES=y" >> $config_host_mak + if test -n "$module_list"; then + echo "CONFIG_MODULE_WHITELIST=$module_list" >> $config_host_mak + fi fi case "$cpu" in arm|i386|x86_64|x32|ppc|aarch64) diff --git a/include/qemu/module.h b/include/qemu/module.h index c4ccd57..6458d8f 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -14,11 +14,25 @@ #ifndef QEMU_MODULE_H #define QEMU_MODULE_H +#ifdef BUILD_DSO + +/* For error message, this function is an identification of qemu module */ +void qemu_module_do_init(void (*init)(void)); + +/* To restrict loading of arbitrary DSO, The init function name is changed per + * "./configure" to refuse unknown DSO file */ +void DSO_INIT_FUN(void); + +#define module_init(function, type) \ +void qemu_module_do_init(void (*init)(void)) { init(); } \ +void DSO_INIT_FUN(void) { qemu_module_do_init(function); } +#else /* This should not be used directly. Use block_init etc. instead. */ #define module_init(function, type) \ static void __attribute__((constructor)) do_qemu_init_ ## function(void) { \ register_module_init(function, type); \ } +#endif typedef enum { MODULE_INIT_BLOCK, @@ -37,4 +51,13 @@ void register_module_init(void (*fn)(void), module_init_type type); void module_call_init(module_init_type type); +typedef enum { + MODULE_LOAD_BLOCK = 0, + MODULE_LOAD_UI, + MODULE_LOAD_NET, + MODULE_LOAD_MAX, +} module_load_type; + +void module_load(module_load_type type); + #endif diff --git a/rules.mak b/rules.mak index 43a502e..e5529da 100644 --- a/rules.mak +++ b/rules.mak @@ -62,7 +62,7 @@ endif %.o: %.dtrace $(call quiet-command,dtrace -o $@ -G -s $<, " GEN $(TARGET_DIR)$@") -%$(DSOSUF): QEMU_CFLAGS += -fPIC +%$(DSOSUF): QEMU_CFLAGS += -fPIC -DBUILD_DSO %$(DSOSUF): LDFLAGS += $(LDFLAGS_SHARED) %$(DSOSUF): %.mo libqemustub.a $(call LINK,$^) @@ -165,13 +165,18 @@ $(if $(nested-dirs), $(call unnest-vars-1)) endef +is-whitelisted = $(if $(CONFIG_MODULE_WHITELIST),$(strip \ + $(filter $(CONFIG_MODULE_WHITELIST),$(basename $(notdir $1)))),\ + yes) define add-modules $(foreach o,$(filter %.o,$($1)), $(eval $(patsubst %.o,%.mo,$o): $o) \ $(eval $(patsubst %.o,%.mo,$o)-objs := $o)) $(foreach o,$(filter %.mo,$($1)),$(eval \ $o: $($o-objs))) -$(eval modules-m += $(patsubst %.o,%.mo,$($1))) +$(eval t := $(patsubst %.o,%.mo,$($1))) +$(foreach o,$t,$(if $(call is-whitelisted,$o),$(eval \ + modules-m += $o))) endef define unnest-vars diff --git a/scripts/create_config b/scripts/create_config index b1adbf5..ab430c7 100755 --- a/scripts/create_config +++ b/scripts/create_config @@ -26,6 +26,24 @@ case $line in # save for the next definitions prefix=${line#*=} ;; + moddir=*) + eval "moddir=\"${line#*=}\"" + echo "#define CONFIG_MODDIR \"$moddir\"" + ;; + CONFIG_FINGERPRINT=*) + echo "#define DSO_INIT_FUN init_${line#*=}" + echo "#define DSO_INIT_FUN_STR \"init_${line#*=}\"" + ;; + CONFIG_MODULES=*) + echo "#define CONFIG_MODULES \"${line#*=}\"" + ;; + CONFIG_MODULE_WHITELIST=*) + echo "#define CONFIG_MODULE_WHITELIST\\" + for mod in ${line#*=}; do + echo " \"${mod}\",\\" + done + echo " NULL" + ;; CONFIG_AUDIO_DRIVERS=*) drivers=${line#*=} echo "#define CONFIG_AUDIO_DRIVERS \\" @@ -104,6 +122,9 @@ case $line in value=${line#*=} echo "#define $name $value" ;; + DSOSUF=*) + echo "#define HOST_DSOSUF \"${line#*=}\"" + ;; esac done # read diff --git a/util/module.c b/util/module.c index 7acc33d..1f38311 100644 --- a/util/module.c +++ b/util/module.c @@ -13,6 +13,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include <gmodule.h> #include "qemu-common.h" #include "qemu/queue.h" #include "qemu/module.h" @@ -79,3 +80,76 @@ void module_call_init(module_init_type type) e->init(); } } + +#ifdef CONFIG_MODULES +static void module_load_file(const char *fname) +{ + GModule *g_module; + void (*init_fun)(void); + const char *dsosuf = HOST_DSOSUF; + int len = strlen(fname); + int suf_len = strlen(dsosuf); + + if (len <= suf_len || strcmp(&fname[len - suf_len], dsosuf)) { + /* wrong suffix */ + return; + } + g_module = g_module_open(fname, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + if (!g_module) { + fprintf(stderr, "Failed to open module file %s\n", + g_module_error()); + return; + } + if (!g_module_symbol(g_module, DSO_INIT_FUN_STR, (gpointer *)&init_fun)) { + fprintf(stderr, "Failed to initialize module: %s\n", + fname); + /* Print some info if this is a QEMU module (but from different build), + * this will make debugging user problems easier. */ + if (g_module_symbol(g_module, "qemu_module_do_init", + (gpointer *)&init_fun)) { + fprintf(stderr, + "Note: only modules from the same build can be loaded.\n"); + } + g_module_close(g_module); + return; + } + init_fun(); +} +#endif + +void module_load(module_load_type type) +{ +#ifdef CONFIG_MODULES + const char *path; + char *fname = NULL; + const char **mp; + const char *module_whitelist[] = { + CONFIG_MODULE_WHITELIST + }; + + if (!g_module_supported()) { + return; + } + + switch (type) { + case MODULE_LOAD_BLOCK: + path = CONFIG_MODDIR "/block/"; + break; + case MODULE_LOAD_UI: + path = CONFIG_MODDIR "/ui/"; + break; + case MODULE_LOAD_NET: + path = CONFIG_MODDIR "/net/"; + break; + default: + return; + } + + for (mp = &module_whitelist[0]; *mp; mp++) { + fname = g_strdup_printf("%s%s" HOST_DSOSUF, path, *mp); + module_load_file(fname); + g_free(fname); + } + +#endif +} diff --git a/vl.c b/vl.c index b4b119a..659e53a 100644 --- a/vl.c +++ b/vl.c @@ -2940,6 +2940,8 @@ int main(int argc, char **argv, char **envp) #endif } + module_load(MODULE_LOAD_UI); + module_load(MODULE_LOAD_NET); module_call_init(MODULE_INIT_QOM); qemu_add_opts(&qemu_drive_opts);
Added three types of modules: typedef enum { MODULE_LOAD_BLOCK = 0, MODULE_LOAD_UI, MODULE_LOAD_NET, MODULE_LOAD_MAX, } module_load_type; and their loading function: void module_load(module_load_type). which loads whitelisted ".so" files in a subdir under configured ${MODDIR}, e.g. "/usr/lib/qemu/block". Modules of each type should be loaded in respective subsystem initialization code. The init function of dynamic module is no longer with __attribute__((constructor)) as static linked version, and need to be explicitly called once loaded. The function name is mangled with per configure fingerprint as: init_$(date +%s$$$RANDOM) Which is known to module_load function, and the loading fails if this symbol is not there. With this, modules built from a different tree/version/configure will not be loaded. The module loading code requires gmodule-2.0. Configure option "--enable-modules=L" can be used to restrict qemu to only build/load some whitelisted modules. Signed-off-by: Fam Zheng <famz@redhat.com> --- Makefile | 3 +++ block.c | 1 + configure | 43 ++++++++++++++++++++++-------- include/qemu/module.h | 23 ++++++++++++++++ rules.mak | 9 +++++-- scripts/create_config | 21 +++++++++++++++ util/module.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ vl.c | 2 ++ 8 files changed, 163 insertions(+), 13 deletions(-)