qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 0/5] improve error handling for module load
@ 2022-09-23 23:20 Claudio Fontana
  2022-09-23 23:21 ` [PATCH v6 1/5] module: removed unused function argument "mayfail" Claudio Fontana
                   ` (4 more replies)
  0 siblings, 5 replies; 18+ messages in thread
From: Claudio Fontana @ 2022-09-23 23:20 UTC (permalink / raw)
  To: Paolo Bonzini, Richard Henderson, Markus Armbruster, Kevin Wolf
  Cc: qemu-devel, dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P . Berrangé, Philippe Mathieu-Daudé,
	Claudio Fontana

CHANGELOG:

v5 -> v6:

* added a patch by Kevin to handle the dmg warning about missing
  decompression submodules. (Kevin)

* added more verbose comments about all the affected callers of module_load
  and module_load_qom (Markus)

(OPEN ISSUE): change abort() to exit() when type not present even after loading module?

(Philippe)

v4 -> v5:

* added a patch to rename module_load_one and friends to module_load

* qdev_new: just reuse module_object_class_by_name, to avoid duplicating code

* changed return value of module_load to an int:
  -1 error (Error **errp set).
   0 module or dependencies not installed,
   1 loaded
   2 already loaded (or built-in)

   Adapted all callers.

* module_load: fixed some pre-existing memory leaks, used an out: label
  to do the cleanup.

v3 -> v4: (Richard)

* module_object_class_by_name: return NULL immediately on load error.
* audio_driver_lookup: same.
* bdrv_find_format: same.

* dmg_open: handle optional compression submodules better: f.e.,
  if "dmg-bz2" is not present, continue but offer a warning.
  If "dmg-bz2" load fails with error, error out and return.

* module_load_dso: add newline to error_append_hint.

v2 -> v3:

* take the file existence check outside of module_load_file,
  rename module_load_file to module_load_dso, will be called only on
  an existing file. This will simplify the return value. (Richard)

* move exported function documentation into header files (Richard)

v1 -> v2:

* do not treat the display help text any differently and do report
  module load _errors_. If the module does not exist (ENOENT, ENOTDIR),
  no error will be produced.

DESCRIPTION:

while investigating a permission issue in accel, where accel-tcg-x86_64.so
was not accessible, I noticed that no errors were produced regarding the
module load failure.

This series attempts to improve module_load_one and module_load_qom_one
to handle the error cases better and produce some errors.

Patch 1 is already reviewed and is about removing an unused existing
argument "mayfail" from the call stack.

Patch 2 is the real meat, and that one I would say is RFC.
Will follow up with comments on the specific questions I have.

Patch 3 finally adds a simple check in accel/, aborting if a module
is not found, but relying on the existing error report from
module_load_qom_one.

Claudio Fontana (4):
  module: removed unused function argument "mayfail"
  module: rename module_load_one to module_load
  module: add Error arguments to module_load and module_load_qom
  accel: abort if we fail to load the accelerator plugin

Kevin Wolf (1):
  dmg: warn when opening dmg images containing blocks of unknown type

 accel/accel-softmmu.c |   8 +-
 audio/audio.c         |  16 ++--
 block.c               |  21 ++++-
 block/dmg.c           |  31 +++++-
 hw/core/qdev.c        |   5 +-
 include/qemu/module.h |  37 +++++++-
 qom/object.c          |  18 +++-
 softmmu/qtest.c       |   8 +-
 ui/console.c          |  18 +++-
 util/module.c         | 213 +++++++++++++++++++++++-------------------
 10 files changed, 249 insertions(+), 126 deletions(-)

-- 
2.26.2



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

* [PATCH v6 1/5] module: removed unused function argument "mayfail"
  2022-09-23 23:20 [PATCH v6 0/5] improve error handling for module load Claudio Fontana
@ 2022-09-23 23:21 ` Claudio Fontana
  2022-09-23 23:21 ` [PATCH v6 2/5] module: rename module_load_one to module_load Claudio Fontana
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 18+ messages in thread
From: Claudio Fontana @ 2022-09-23 23:21 UTC (permalink / raw)
  To: Paolo Bonzini, Richard Henderson, Markus Armbruster, Kevin Wolf
  Cc: qemu-devel, dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P . Berrangé, Philippe Mathieu-Daudé,
	Claudio Fontana

mayfail is always passed as false for every invocation throughout the program.
It controls whether to printf or not to printf an error on
g_module_open failure.

Remove this unused argument.

Signed-off-by: Claudio Fontana <cfontana@suse.de>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
---
 include/qemu/module.h |  8 ++++----
 softmmu/qtest.c       |  2 +-
 util/module.c         | 20 +++++++++-----------
 3 files changed, 14 insertions(+), 16 deletions(-)

diff --git a/include/qemu/module.h b/include/qemu/module.h
index bd73607104..8c012bbe03 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -61,15 +61,15 @@ typedef enum {
 #define fuzz_target_init(function) module_init(function, \
                                                MODULE_INIT_FUZZ_TARGET)
 #define migration_init(function) module_init(function, MODULE_INIT_MIGRATION)
-#define block_module_load_one(lib) module_load_one("block-", lib, false)
-#define ui_module_load_one(lib) module_load_one("ui-", lib, false)
-#define audio_module_load_one(lib) module_load_one("audio-", lib, false)
+#define block_module_load_one(lib) module_load_one("block-", lib)
+#define ui_module_load_one(lib) module_load_one("ui-", lib)
+#define audio_module_load_one(lib) module_load_one("audio-", lib)
 
 void register_module_init(void (*fn)(void), module_init_type type);
 void register_dso_module_init(void (*fn)(void), module_init_type type);
 
 void module_call_init(module_init_type type);
-bool module_load_one(const char *prefix, const char *lib_name, bool mayfail);
+bool module_load_one(const char *prefix, const char *lib_name);
 void module_load_qom_one(const char *type);
 void module_load_qom_all(void);
 void module_allow_arch(const char *arch);
diff --git a/softmmu/qtest.c b/softmmu/qtest.c
index f8acef2628..76eb7bac56 100644
--- a/softmmu/qtest.c
+++ b/softmmu/qtest.c
@@ -756,7 +756,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
         g_assert(words[1] && words[2]);
 
         qtest_send_prefix(chr);
-        if (module_load_one(words[1], words[2], false)) {
+        if (module_load_one(words[1], words[2])) {
             qtest_sendf(chr, "OK\n");
         } else {
             qtest_sendf(chr, "FAIL\n");
diff --git a/util/module.c b/util/module.c
index 8ddb0e18f5..8563edd626 100644
--- a/util/module.c
+++ b/util/module.c
@@ -144,7 +144,7 @@ static bool module_check_arch(const QemuModinfo *modinfo)
     return true;
 }
 
-static int module_load_file(const char *fname, bool mayfail, bool export_symbols)
+static int module_load_file(const char *fname, bool export_symbols)
 {
     GModule *g_module;
     void (*sym)(void);
@@ -172,10 +172,8 @@ static int module_load_file(const char *fname, bool mayfail, bool export_symbols
     }
     g_module = g_module_open(fname, flags);
     if (!g_module) {
-        if (!mayfail) {
-            fprintf(stderr, "Failed to open module: %s\n",
-                    g_module_error());
-        }
+        fprintf(stderr, "Failed to open module: %s\n",
+                g_module_error());
         ret = -EINVAL;
         goto out;
     }
@@ -208,7 +206,7 @@ out:
 }
 #endif
 
-bool module_load_one(const char *prefix, const char *lib_name, bool mayfail)
+bool module_load_one(const char *prefix, const char *lib_name)
 {
     bool success = false;
 
@@ -256,7 +254,7 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail)
             if (strcmp(modinfo->name, module_name) == 0) {
                 /* we depend on other module(s) */
                 for (sl = modinfo->deps; *sl != NULL; sl++) {
-                    module_load_one("", *sl, false);
+                    module_load_one("", *sl);
                 }
             } else {
                 for (sl = modinfo->deps; *sl != NULL; sl++) {
@@ -287,7 +285,7 @@ bool module_load_one(const char *prefix, const char *lib_name, bool mayfail)
     for (i = 0; i < n_dirs; i++) {
         fname = g_strdup_printf("%s/%s%s",
                 dirs[i], module_name, CONFIG_HOST_DSOSUF);
-        ret = module_load_file(fname, mayfail, export_symbols);
+        ret = module_load_file(fname, export_symbols);
         g_free(fname);
         fname = NULL;
         /* Try loading until loaded a module file */
@@ -333,7 +331,7 @@ void module_load_qom_one(const char *type)
         }
         for (sl = modinfo->objs; *sl != NULL; sl++) {
             if (strcmp(type, *sl) == 0) {
-                module_load_one("", modinfo->name, false);
+                module_load_one("", modinfo->name);
             }
         }
     }
@@ -354,7 +352,7 @@ void module_load_qom_all(void)
         if (!module_check_arch(modinfo)) {
             continue;
         }
-        module_load_one("", modinfo->name, false);
+        module_load_one("", modinfo->name);
     }
     module_loaded_qom_all = true;
 }
@@ -370,7 +368,7 @@ void qemu_load_module_for_opts(const char *group)
         }
         for (sl = modinfo->opts; *sl != NULL; sl++) {
             if (strcmp(group, *sl) == 0) {
-                module_load_one("", modinfo->name, false);
+                module_load_one("", modinfo->name);
             }
         }
     }
-- 
2.26.2



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

* [PATCH v6 2/5] module: rename module_load_one to module_load
  2022-09-23 23:20 [PATCH v6 0/5] improve error handling for module load Claudio Fontana
  2022-09-23 23:21 ` [PATCH v6 1/5] module: removed unused function argument "mayfail" Claudio Fontana
@ 2022-09-23 23:21 ` Claudio Fontana
  2022-09-23 23:21 ` [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom Claudio Fontana
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 18+ messages in thread
From: Claudio Fontana @ 2022-09-23 23:21 UTC (permalink / raw)
  To: Paolo Bonzini, Richard Henderson, Markus Armbruster, Kevin Wolf
  Cc: qemu-devel, dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P . Berrangé, Philippe Mathieu-Daudé,
	Claudio Fontana

Signed-off-by: Claudio Fontana <cfontana@suse.de>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
---
 audio/audio.c         |  2 +-
 block.c               |  4 ++--
 block/dmg.c           |  4 ++--
 hw/core/qdev.c        |  2 +-
 include/qemu/module.h | 10 +++++-----
 qom/object.c          |  4 ++--
 softmmu/qtest.c       |  2 +-
 ui/console.c          |  6 +++---
 util/module.c         | 14 +++++++-------
 9 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index 76b8735b44..0a682336a0 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -79,7 +79,7 @@ audio_driver *audio_driver_lookup(const char *name)
         }
     }
 
-    audio_module_load_one(name);
+    audio_module_load(name);
     QLIST_FOREACH(d, &audio_drivers, next) {
         if (strcmp(name, d->name) == 0) {
             return d;
diff --git a/block.c b/block.c
index bc85f46eed..72c7f6d47d 100644
--- a/block.c
+++ b/block.c
@@ -464,7 +464,7 @@ BlockDriver *bdrv_find_format(const char *format_name)
     /* The driver isn't registered, maybe we need to load a module */
     for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) {
         if (!strcmp(block_driver_modules[i].format_name, format_name)) {
-            block_module_load_one(block_driver_modules[i].library_name);
+            block_module_load(block_driver_modules[i].library_name);
             break;
         }
     }
@@ -976,7 +976,7 @@ BlockDriver *bdrv_find_protocol(const char *filename,
     for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) {
         if (block_driver_modules[i].protocol_name &&
             !strcmp(block_driver_modules[i].protocol_name, protocol)) {
-            block_module_load_one(block_driver_modules[i].library_name);
+            block_module_load(block_driver_modules[i].library_name);
             break;
         }
     }
diff --git a/block/dmg.c b/block/dmg.c
index 98db18d82a..007b8d9996 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -446,8 +446,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
         return -EINVAL;
     }
 
-    block_module_load_one("dmg-bz2");
-    block_module_load_one("dmg-lzfse");
+    block_module_load("dmg-bz2");
+    block_module_load("dmg-lzfse");
 
     s->n_chunks = 0;
     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 0806d8fcaa..25dfc08468 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -148,7 +148,7 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp)
 DeviceState *qdev_new(const char *name)
 {
     if (!object_class_by_name(name)) {
-        module_load_qom_one(name);
+        module_load_qom(name);
     }
     return DEVICE(object_new(name));
 }
diff --git a/include/qemu/module.h b/include/qemu/module.h
index 8c012bbe03..b7911ce791 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -61,16 +61,16 @@ typedef enum {
 #define fuzz_target_init(function) module_init(function, \
                                                MODULE_INIT_FUZZ_TARGET)
 #define migration_init(function) module_init(function, MODULE_INIT_MIGRATION)
-#define block_module_load_one(lib) module_load_one("block-", lib)
-#define ui_module_load_one(lib) module_load_one("ui-", lib)
-#define audio_module_load_one(lib) module_load_one("audio-", lib)
+#define block_module_load(lib) module_load("block-", lib)
+#define ui_module_load(lib) module_load("ui-", lib)
+#define audio_module_load(lib) module_load("audio-", lib)
 
 void register_module_init(void (*fn)(void), module_init_type type);
 void register_dso_module_init(void (*fn)(void), module_init_type type);
 
 void module_call_init(module_init_type type);
-bool module_load_one(const char *prefix, const char *lib_name);
-void module_load_qom_one(const char *type);
+bool module_load(const char *prefix, const char *lib_name);
+void module_load_qom(const char *type);
 void module_load_qom_all(void);
 void module_allow_arch(const char *arch);
 
diff --git a/qom/object.c b/qom/object.c
index d34608558e..4f834f3bf6 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -526,7 +526,7 @@ void object_initialize(void *data, size_t size, const char *typename)
 
 #ifdef CONFIG_MODULES
     if (!type) {
-        module_load_qom_one(typename);
+        module_load_qom(typename);
         type = type_get_by_name(typename);
     }
 #endif
@@ -1033,7 +1033,7 @@ ObjectClass *module_object_class_by_name(const char *typename)
     oc = object_class_by_name(typename);
 #ifdef CONFIG_MODULES
     if (!oc) {
-        module_load_qom_one(typename);
+        module_load_qom(typename);
         oc = object_class_by_name(typename);
     }
 #endif
diff --git a/softmmu/qtest.c b/softmmu/qtest.c
index 76eb7bac56..fc5b733c63 100644
--- a/softmmu/qtest.c
+++ b/softmmu/qtest.c
@@ -756,7 +756,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
         g_assert(words[1] && words[2]);
 
         qtest_send_prefix(chr);
-        if (module_load_one(words[1], words[2])) {
+        if (module_load(words[1], words[2])) {
             qtest_sendf(chr, "OK\n");
         } else {
             qtest_sendf(chr, "FAIL\n");
diff --git a/ui/console.c b/ui/console.c
index 765892f84f..4913c55684 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -2632,7 +2632,7 @@ bool qemu_display_find_default(DisplayOptions *opts)
 
     for (i = 0; i < (int)ARRAY_SIZE(prio); i++) {
         if (dpys[prio[i]] == NULL) {
-            ui_module_load_one(DisplayType_str(prio[i]));
+            ui_module_load(DisplayType_str(prio[i]));
         }
         if (dpys[prio[i]] == NULL) {
             continue;
@@ -2650,7 +2650,7 @@ void qemu_display_early_init(DisplayOptions *opts)
         return;
     }
     if (dpys[opts->type] == NULL) {
-        ui_module_load_one(DisplayType_str(opts->type));
+        ui_module_load(DisplayType_str(opts->type));
     }
     if (dpys[opts->type] == NULL) {
         error_report("Display '%s' is not available.",
@@ -2680,7 +2680,7 @@ void qemu_display_help(void)
     printf("none\n");
     for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) {
         if (!dpys[idx]) {
-            ui_module_load_one(DisplayType_str(idx));
+            ui_module_load(DisplayType_str(idx));
         }
         if (dpys[idx]) {
             printf("%s\n",  DisplayType_str(dpys[idx]->type));
diff --git a/util/module.c b/util/module.c
index 8563edd626..ad89cd50dc 100644
--- a/util/module.c
+++ b/util/module.c
@@ -206,7 +206,7 @@ out:
 }
 #endif
 
-bool module_load_one(const char *prefix, const char *lib_name)
+bool module_load(const char *prefix, const char *lib_name)
 {
     bool success = false;
 
@@ -254,7 +254,7 @@ bool module_load_one(const char *prefix, const char *lib_name)
             if (strcmp(modinfo->name, module_name) == 0) {
                 /* we depend on other module(s) */
                 for (sl = modinfo->deps; *sl != NULL; sl++) {
-                    module_load_one("", *sl);
+                    module_load("", *sl);
                 }
             } else {
                 for (sl = modinfo->deps; *sl != NULL; sl++) {
@@ -312,7 +312,7 @@ bool module_load_one(const char *prefix, const char *lib_name)
 
 static bool module_loaded_qom_all;
 
-void module_load_qom_one(const char *type)
+void module_load_qom(const char *type)
 {
     const QemuModinfo *modinfo;
     const char **sl;
@@ -331,7 +331,7 @@ void module_load_qom_one(const char *type)
         }
         for (sl = modinfo->objs; *sl != NULL; sl++) {
             if (strcmp(type, *sl) == 0) {
-                module_load_one("", modinfo->name);
+                module_load("", modinfo->name);
             }
         }
     }
@@ -352,7 +352,7 @@ void module_load_qom_all(void)
         if (!module_check_arch(modinfo)) {
             continue;
         }
-        module_load_one("", modinfo->name);
+        module_load("", modinfo->name);
     }
     module_loaded_qom_all = true;
 }
@@ -368,7 +368,7 @@ void qemu_load_module_for_opts(const char *group)
         }
         for (sl = modinfo->opts; *sl != NULL; sl++) {
             if (strcmp(group, *sl) == 0) {
-                module_load_one("", modinfo->name);
+                module_load("", modinfo->name);
             }
         }
     }
@@ -378,7 +378,7 @@ void qemu_load_module_for_opts(const char *group)
 
 void module_allow_arch(const char *arch) {}
 void qemu_load_module_for_opts(const char *group) {}
-void module_load_qom_one(const char *type) {}
+void module_load_qom(const char *type) {}
 void module_load_qom_all(void) {}
 
 #endif
-- 
2.26.2



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

* [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom
  2022-09-23 23:20 [PATCH v6 0/5] improve error handling for module load Claudio Fontana
  2022-09-23 23:21 ` [PATCH v6 1/5] module: removed unused function argument "mayfail" Claudio Fontana
  2022-09-23 23:21 ` [PATCH v6 2/5] module: rename module_load_one to module_load Claudio Fontana
@ 2022-09-23 23:21 ` Claudio Fontana
  2022-09-26 10:38   ` Kevin Wolf
  2022-09-23 23:21 ` [PATCH v6 4/5] dmg: warn when opening dmg images containing blocks of unknown type Claudio Fontana
  2022-09-23 23:21 ` [PATCH v6 5/5] accel: abort if we fail to load the accelerator plugin Claudio Fontana
  4 siblings, 1 reply; 18+ messages in thread
From: Claudio Fontana @ 2022-09-23 23:21 UTC (permalink / raw)
  To: Paolo Bonzini, Richard Henderson, Markus Armbruster, Kevin Wolf
  Cc: qemu-devel, dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P . Berrangé, Philippe Mathieu-Daudé,
	Claudio Fontana

improve error handling during module load, by changing:

bool module_load(const char *prefix, const char *lib_name);
void module_load_qom(const char *type);

to:

int module_load(const char *prefix, const char *name, Error **errp);
int module_load_qom(const char *type, Error **errp);

where the return value is:

 -1 on module load error, and errp is set with the error
  0 on module or one of its dependencies are not installed
  1 on module load success
  2 on module load success (module already loaded or built-in)

module_load_qom_one has been introduced in:

commit 28457744c345 ("module: qom module support"), which built on top of
module_load_one, but discarded the bool return value. Restore it.

Adapt all callers to emit errors, or ignore them, or fail hard,
as appropriate in each context.

Some memory leaks also fixed as part of the module_load changes.

audio: when attempting to load an audio module, report module load errors.
block: when attempting to load a block module, report module load errors.
console: when attempting to load a display module, report module load errors.

qdev: when creating a new qdev Device object (DeviceState), report load errors.
      If a module cannot be loaded to create that device, now abort execution.

qom/object.c: when initializing a QOM object, or looking up class_by_name,
              report module load errors.

qtest: when processing the "module_load" qtest command, report errors
       in the load of the module.

Signed-off-by: Claudio Fontana <cfontana@suse.de>
---
 audio/audio.c         |  16 ++--
 block.c               |  21 ++++-
 block/dmg.c           |  12 ++-
 hw/core/qdev.c        |   5 +-
 include/qemu/module.h |  37 +++++++-
 qom/object.c          |  18 +++-
 softmmu/qtest.c       |   8 +-
 ui/console.c          |  18 +++-
 util/module.c         | 211 +++++++++++++++++++++++-------------------
 9 files changed, 223 insertions(+), 123 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index 0a682336a0..ea51793843 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -72,20 +72,24 @@ void audio_driver_register(audio_driver *drv)
 audio_driver *audio_driver_lookup(const char *name)
 {
     struct audio_driver *d;
+    Error *local_err = NULL;
+    int rv;
 
     QLIST_FOREACH(d, &audio_drivers, next) {
         if (strcmp(name, d->name) == 0) {
             return d;
         }
     }
-
-    audio_module_load(name);
-    QLIST_FOREACH(d, &audio_drivers, next) {
-        if (strcmp(name, d->name) == 0) {
-            return d;
+    rv = audio_module_load(name, &local_err);
+    if (rv > 0) {
+        QLIST_FOREACH(d, &audio_drivers, next) {
+            if (strcmp(name, d->name) == 0) {
+                return d;
+            }
         }
+    } else if (rv < 0) {
+        error_report_err(local_err);
     }
-
     return NULL;
 }
 
diff --git a/block.c b/block.c
index 72c7f6d47d..0390ece056 100644
--- a/block.c
+++ b/block.c
@@ -464,12 +464,18 @@ BlockDriver *bdrv_find_format(const char *format_name)
     /* The driver isn't registered, maybe we need to load a module */
     for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) {
         if (!strcmp(block_driver_modules[i].format_name, format_name)) {
-            block_module_load(block_driver_modules[i].library_name);
+            Error *local_err = NULL;
+            int rv = block_module_load(block_driver_modules[i].library_name,
+                                       &local_err);
+            if (rv > 0) {
+                return bdrv_do_find_format(format_name);
+            } else if (rv < 0) {
+                error_report_err(local_err);
+            }
             break;
         }
     }
-
-    return bdrv_do_find_format(format_name);
+    return NULL;
 }
 
 static int bdrv_format_is_whitelisted(const char *format_name, bool read_only)
@@ -976,12 +982,17 @@ BlockDriver *bdrv_find_protocol(const char *filename,
     for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) {
         if (block_driver_modules[i].protocol_name &&
             !strcmp(block_driver_modules[i].protocol_name, protocol)) {
-            block_module_load(block_driver_modules[i].library_name);
+            Error *local_err = NULL;
+            int rv = block_module_load(block_driver_modules[i].library_name, &local_err);
+            if (rv > 0) {
+                drv1 = bdrv_do_find_protocol(protocol);
+            } else if (rv < 0) {
+                error_report_err(local_err);
+            }
             break;
         }
     }
 
-    drv1 = bdrv_do_find_protocol(protocol);
     if (!drv1) {
         error_setg(errp, "Unknown protocol '%s'", protocol);
     }
diff --git a/block/dmg.c b/block/dmg.c
index 007b8d9996..a422cf8d5b 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -434,6 +434,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
     uint64_t plist_xml_offset, plist_xml_length;
     int64_t offset;
     int ret;
+    Error *local_err = NULL;
 
     ret = bdrv_apply_auto_read_only(bs, NULL, errp);
     if (ret < 0) {
@@ -446,8 +447,15 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
         return -EINVAL;
     }
 
-    block_module_load("dmg-bz2");
-    block_module_load("dmg-lzfse");
+    if (block_module_load("dmg-bz2", &local_err) < 0) {
+        error_report_err(local_err);
+        return -EINVAL;
+    }
+    local_err = NULL;
+    if (block_module_load("dmg-lzfse", &local_err) < 0) {
+        error_report_err(local_err);
+        return -EINVAL;
+    }
 
     s->n_chunks = 0;
     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 25dfc08468..06a4bce145 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -147,8 +147,9 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp)
 
 DeviceState *qdev_new(const char *name)
 {
-    if (!object_class_by_name(name)) {
-        module_load_qom(name);
+    if (!module_object_class_by_name(name)) {
+        error_report("could not find a module for type '%s'", name);
+        abort();
     }
     return DEVICE(object_new(name));
 }
diff --git a/include/qemu/module.h b/include/qemu/module.h
index b7911ce791..c37ce74b16 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -61,16 +61,43 @@ typedef enum {
 #define fuzz_target_init(function) module_init(function, \
                                                MODULE_INIT_FUZZ_TARGET)
 #define migration_init(function) module_init(function, MODULE_INIT_MIGRATION)
-#define block_module_load(lib) module_load("block-", lib)
-#define ui_module_load(lib) module_load("ui-", lib)
-#define audio_module_load(lib) module_load("audio-", lib)
+#define block_module_load(lib, errp) module_load("block-", lib, errp)
+#define ui_module_load(lib, errp) module_load("ui-", lib, errp)
+#define audio_module_load(lib, errp) module_load("audio-", lib, errp)
 
 void register_module_init(void (*fn)(void), module_init_type type);
 void register_dso_module_init(void (*fn)(void), module_init_type type);
 
 void module_call_init(module_init_type type);
-bool module_load(const char *prefix, const char *lib_name);
-void module_load_qom(const char *type);
+
+/*
+ * module_load: attempt to load a module from a set of directories
+ *
+ * directories searched are:
+ * - getenv("QEMU_MODULE_DIR")
+ * - get_relocated_path(CONFIG_QEMU_MODDIR);
+ * - /var/run/qemu/${version_dir}
+ *
+ * prefix:         a subsystem prefix, or the empty string ("audio-", ..., "")
+ * name:           name of the module
+ * errp:           error to set in case the module is found, but load failed.
+ *
+ * Return value:   -1 on error (errp set if not NULL).
+ *                 0 if module or one of its dependencies are not installed,
+ *                 1 if the module is found and loaded,
+ *                 2 if the module is already loaded, or module is built-in.
+ */
+int module_load(const char *prefix, const char *name, Error **errp);
+
+/*
+ * module_load_qom: attempt to load a module to provide a QOM type
+ *
+ * type:           the type to be provided
+ * errp:           error to set.
+ *
+ * Return value:   as per module_load.
+ */
+int module_load_qom(const char *type, Error **errp);
 void module_load_qom_all(void);
 void module_allow_arch(const char *arch);
 
diff --git a/qom/object.c b/qom/object.c
index 4f834f3bf6..35ced55282 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -526,8 +526,13 @@ void object_initialize(void *data, size_t size, const char *typename)
 
 #ifdef CONFIG_MODULES
     if (!type) {
-        module_load_qom(typename);
-        type = type_get_by_name(typename);
+        Error *local_err = NULL;
+        int rv = module_load_qom(typename, &local_err);
+        if (rv > 0) {
+            type = type_get_by_name(typename);
+        } else if (rv < 0) {
+            error_report_err(local_err);
+        }
     }
 #endif
     if (!type) {
@@ -1033,8 +1038,13 @@ ObjectClass *module_object_class_by_name(const char *typename)
     oc = object_class_by_name(typename);
 #ifdef CONFIG_MODULES
     if (!oc) {
-        module_load_qom(typename);
-        oc = object_class_by_name(typename);
+        Error *local_err = NULL;
+        int rv = module_load_qom(typename, &local_err);
+        if (rv > 0) {
+            oc = object_class_by_name(typename);
+        } else if (rv < 0) {
+            error_report_err(local_err);
+        }
     }
 #endif
     return oc;
diff --git a/softmmu/qtest.c b/softmmu/qtest.c
index fc5b733c63..36e28609ff 100644
--- a/softmmu/qtest.c
+++ b/softmmu/qtest.c
@@ -753,12 +753,18 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
         qtest_sendf(chr, "OK %"PRIi64"\n",
                     (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
     } else if (strcmp(words[0], "module_load") == 0) {
+        Error *local_err = NULL;
+        int rv;
         g_assert(words[1] && words[2]);
 
         qtest_send_prefix(chr);
-        if (module_load(words[1], words[2])) {
+        rv = module_load(words[1], words[2], &local_err);
+        if (rv > 0) {
             qtest_sendf(chr, "OK\n");
         } else {
+            if (rv < 0) {
+                error_report_err(local_err);
+            }
             qtest_sendf(chr, "FAIL\n");
         }
     } else if (qtest_enabled() && strcmp(words[0], "clock_set") == 0) {
diff --git a/ui/console.c b/ui/console.c
index 4913c55684..4e53c3c71b 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -2632,7 +2632,11 @@ bool qemu_display_find_default(DisplayOptions *opts)
 
     for (i = 0; i < (int)ARRAY_SIZE(prio); i++) {
         if (dpys[prio[i]] == NULL) {
-            ui_module_load(DisplayType_str(prio[i]));
+            Error *local_err = NULL;
+            int rv = ui_module_load(DisplayType_str(prio[i]), &local_err);
+            if (rv < 0) {
+                error_report_err(local_err);
+            }
         }
         if (dpys[prio[i]] == NULL) {
             continue;
@@ -2650,7 +2654,11 @@ void qemu_display_early_init(DisplayOptions *opts)
         return;
     }
     if (dpys[opts->type] == NULL) {
-        ui_module_load(DisplayType_str(opts->type));
+        Error *local_err = NULL;
+        int rv = ui_module_load(DisplayType_str(opts->type), &local_err);
+        if (rv < 0) {
+            error_report_err(local_err);
+        }
     }
     if (dpys[opts->type] == NULL) {
         error_report("Display '%s' is not available.",
@@ -2680,7 +2688,11 @@ void qemu_display_help(void)
     printf("none\n");
     for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) {
         if (!dpys[idx]) {
-            ui_module_load(DisplayType_str(idx));
+            Error *local_err = NULL;
+            int rv = ui_module_load(DisplayType_str(idx), &local_err);
+            if (rv < 0) {
+                error_report_err(local_err);
+            }
         }
         if (dpys[idx]) {
             printf("%s\n",  DisplayType_str(dpys[idx]->type));
diff --git a/util/module.c b/util/module.c
index ad89cd50dc..b67923a986 100644
--- a/util/module.c
+++ b/util/module.c
@@ -21,6 +21,7 @@
 #include "qemu/module.h"
 #include "qemu/cutils.h"
 #include "qemu/config-file.h"
+#include "qapi/error.h"
 #ifdef CONFIG_MODULE_UPGRADES
 #include "qemu-version.h"
 #endif
@@ -144,25 +145,22 @@ static bool module_check_arch(const QemuModinfo *modinfo)
     return true;
 }
 
-static int module_load_file(const char *fname, bool export_symbols)
+/*
+ * module_load_dso: attempt to load an existing dso file
+ *
+ * fname:          full pathname of the file to load
+ * export_symbols: if true, add the symbols to the global name space
+ * errp:           error to set.
+ *
+ * Return value:   true on success, false on error, and errp will be set.
+ */
+static bool module_load_dso(const char *fname, bool export_symbols,
+                            Error **errp)
 {
     GModule *g_module;
     void (*sym)(void);
-    const char *dsosuf = CONFIG_HOST_DSOSUF;
-    int len = strlen(fname);
-    int suf_len = strlen(dsosuf);
     ModuleEntry *e, *next;
-    int ret, flags;
-
-    if (len <= suf_len || strcmp(&fname[len - suf_len], dsosuf)) {
-        /* wrong suffix */
-        ret = -EINVAL;
-        goto out;
-    }
-    if (access(fname, F_OK)) {
-        ret = -ENOENT;
-        goto out;
-    }
+    int flags;
 
     assert(QTAILQ_EMPTY(&dso_init_list));
 
@@ -172,46 +170,38 @@ static int module_load_file(const char *fname, bool export_symbols)
     }
     g_module = g_module_open(fname, flags);
     if (!g_module) {
-        fprintf(stderr, "Failed to open module: %s\n",
-                g_module_error());
-        ret = -EINVAL;
-        goto out;
+        error_setg(errp, "failed to open module: %s", g_module_error());
+        return false;
     }
     if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) {
-        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. */
+        error_setg(errp, "failed to initialize module: %s", 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_dummy", (gpointer *)&sym)) {
-            fprintf(stderr,
-                    "Note: only modules from the same build can be loaded.\n");
+            error_append_hint(errp,
+                "Only modules from the same build can be loaded.\n");
         }
         g_module_close(g_module);
-        ret = -EINVAL;
-    } else {
-        QTAILQ_FOREACH(e, &dso_init_list, node) {
-            e->init();
-            register_module_init(e->init, e->type);
-        }
-        ret = 0;
+        return false;
     }
 
+    QTAILQ_FOREACH(e, &dso_init_list, node) {
+        e->init();
+        register_module_init(e->init, e->type);
+    }
     trace_module_load_module(fname);
     QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) {
         QTAILQ_REMOVE(&dso_init_list, e, node);
         g_free(e);
     }
-out:
-    return ret;
+    return true;
 }
-#endif
 
-bool module_load(const char *prefix, const char *lib_name)
+int module_load(const char *prefix, const char *name, Error **errp)
 {
-    bool success = false;
-
-#ifdef CONFIG_MODULES
-    char *fname = NULL;
+    int rv = -1;
 #ifdef CONFIG_MODULE_UPGRADES
     char *version_dir;
 #endif
@@ -219,54 +209,29 @@ bool module_load(const char *prefix, const char *lib_name)
     char *dirs[5];
     char *module_name;
     int i = 0, n_dirs = 0;
-    int ret;
     bool export_symbols = false;
     static GHashTable *loaded_modules;
     const QemuModinfo *modinfo;
     const char **sl;
 
     if (!g_module_supported()) {
-        fprintf(stderr, "Module is not supported by system.\n");
-        return false;
+        error_setg(errp, "%s", "this platform does not support GLib modules");
+        return -1;
     }
 
     if (!loaded_modules) {
         loaded_modules = g_hash_table_new(g_str_hash, g_str_equal);
     }
 
-    module_name = g_strdup_printf("%s%s", prefix, lib_name);
+    /* allocate all resources managed by the out: label here */
+    module_name = g_strdup_printf("%s%s", prefix, name);
 
     if (g_hash_table_contains(loaded_modules, module_name)) {
         g_free(module_name);
-        return true;
+        return 2; /* module already loaded */
     }
     g_hash_table_add(loaded_modules, module_name);
 
-    for (modinfo = module_info; modinfo->name != NULL; modinfo++) {
-        if (modinfo->arch) {
-            if (strcmp(modinfo->name, module_name) == 0) {
-                if (!module_check_arch(modinfo)) {
-                    return false;
-                }
-            }
-        }
-        if (modinfo->deps) {
-            if (strcmp(modinfo->name, module_name) == 0) {
-                /* we depend on other module(s) */
-                for (sl = modinfo->deps; *sl != NULL; sl++) {
-                    module_load("", *sl);
-                }
-            } else {
-                for (sl = modinfo->deps; *sl != NULL; sl++) {
-                    if (strcmp(module_name, *sl) == 0) {
-                        /* another module depends on us */
-                        export_symbols = true;
-                    }
-                }
-            }
-        }
-    }
-
     search_dir = getenv("QEMU_MODULE_DIR");
     if (search_dir != NULL) {
         dirs[n_dirs++] = g_strdup_printf("%s", search_dir);
@@ -279,46 +244,87 @@ bool module_load(const char *prefix, const char *lib_name)
                              '_');
     dirs[n_dirs++] = g_strdup_printf("/var/run/qemu/%s", version_dir);
 #endif
-
     assert(n_dirs <= ARRAY_SIZE(dirs));
 
-    for (i = 0; i < n_dirs; i++) {
-        fname = g_strdup_printf("%s/%s%s",
-                dirs[i], module_name, CONFIG_HOST_DSOSUF);
-        ret = module_load_file(fname, export_symbols);
-        g_free(fname);
-        fname = NULL;
-        /* Try loading until loaded a module file */
-        if (!ret) {
-            success = true;
-            break;
+    /* end of resources managed by the out: label */
+
+    for (modinfo = module_info; modinfo->name != NULL; modinfo++) {
+        if (modinfo->arch) {
+            if (strcmp(modinfo->name, module_name) == 0) {
+                if (!module_check_arch(modinfo)) {
+                    error_setg(errp, "module arch does not match: "
+                        "expected '%s', got '%s'", module_arch, modinfo->arch);
+                    goto out;
+                }
+            }
+        }
+        if (modinfo->deps) {
+            if (strcmp(modinfo->name, module_name) == 0) {
+                /* we depend on other module(s) */
+                for (sl = modinfo->deps; *sl != NULL; sl++) {
+                    int subrv = module_load("", *sl, errp);
+                    if (subrv <= 0) {
+                        rv = subrv;
+                        goto out;
+                    }
+                }
+            } else {
+                for (sl = modinfo->deps; *sl != NULL; sl++) {
+                    if (strcmp(module_name, *sl) == 0) {
+                        /* another module depends on us */
+                        export_symbols = true;
+                    }
+                }
+            }
         }
     }
 
-    if (!success) {
-        g_hash_table_remove(loaded_modules, module_name);
-        g_free(module_name);
+    for (i = 0; i < n_dirs; i++) {
+        char *fname = g_strdup_printf("%s/%s%s",
+                                      dirs[i], module_name, CONFIG_HOST_DSOSUF);
+        int ret = access(fname, F_OK);
+        if (ret != 0 && (errno == ENOENT || errno == ENOTDIR)) {
+            /*
+             * if we don't find the module in this dir, try the next one.
+             * If we don't find it in any dir, that can be fine too: user
+             * did not install the module. We will return 0 in this case
+             * with no error set.
+             */
+            g_free(fname);
+            continue;
+        } else if (ret != 0) {
+            /* most common is EACCES here */
+            error_setg_errno(errp, errno, "error trying to access %s", fname);
+        } else if (module_load_dso(fname, export_symbols, errp)) {
+            rv = 1; /* module successfully loaded */
+        }
+        g_free(fname);
+        goto out;
     }
+    rv = 0; /* module not found */
 
+out:
+    if (rv <= 0) {
+        g_hash_table_remove(loaded_modules, module_name);
+    }
+    g_free(module_name);
     for (i = 0; i < n_dirs; i++) {
         g_free(dirs[i]);
     }
-
-#endif
-    return success;
+    return rv;
 }
 
-#ifdef CONFIG_MODULES
-
 static bool module_loaded_qom_all;
 
-void module_load_qom(const char *type)
+int module_load_qom(const char *type, Error **errp)
 {
     const QemuModinfo *modinfo;
     const char **sl;
+    int rv = 0;
 
     if (!type) {
-        return;
+        error_setg(errp, "%s", "type is NULL");
+        return -1;
     }
 
     trace_module_lookup_object_type(type);
@@ -331,15 +337,24 @@ void module_load_qom(const char *type)
         }
         for (sl = modinfo->objs; *sl != NULL; sl++) {
             if (strcmp(type, *sl) == 0) {
-                module_load("", modinfo->name);
+                if (rv > 0) {
+                    error_setg(errp, "multiple modules providing '%s'", type);
+                    return -1;
+                }
+                rv = module_load("", modinfo->name, errp);
+                if (rv < 0) {
+                    return rv;
+                }
             }
         }
     }
+    return rv;
 }
 
 void module_load_qom_all(void)
 {
     const QemuModinfo *modinfo;
+    Error *local_err = NULL;
 
     if (module_loaded_qom_all) {
         return;
@@ -352,7 +367,9 @@ void module_load_qom_all(void)
         if (!module_check_arch(modinfo)) {
             continue;
         }
-        module_load("", modinfo->name);
+        if (module_load("", modinfo->name, &local_err) < 0) {
+            error_report_err(local_err);
+        }
     }
     module_loaded_qom_all = true;
 }
@@ -368,7 +385,10 @@ void qemu_load_module_for_opts(const char *group)
         }
         for (sl = modinfo->opts; *sl != NULL; sl++) {
             if (strcmp(group, *sl) == 0) {
-                module_load("", modinfo->name);
+                Error *local_err = NULL;
+                if (module_load("", modinfo->name, &local_err) < 0) {
+                    error_report_err(local_err);
+                }
             }
         }
     }
@@ -378,7 +398,8 @@ void qemu_load_module_for_opts(const char *group)
 
 void module_allow_arch(const char *arch) {}
 void qemu_load_module_for_opts(const char *group) {}
-void module_load_qom(const char *type) {}
+int module_load(const char *prefix, const char *name, Error **errp) { return 2; }
+int module_load_qom(const char *type, Error **errp) { return 2; }
 void module_load_qom_all(void) {}
 
 #endif
-- 
2.26.2



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

* [PATCH v6 4/5] dmg: warn when opening dmg images containing blocks of unknown type
  2022-09-23 23:20 [PATCH v6 0/5] improve error handling for module load Claudio Fontana
                   ` (2 preceding siblings ...)
  2022-09-23 23:21 ` [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom Claudio Fontana
@ 2022-09-23 23:21 ` Claudio Fontana
  2022-09-23 23:21 ` [PATCH v6 5/5] accel: abort if we fail to load the accelerator plugin Claudio Fontana
  4 siblings, 0 replies; 18+ messages in thread
From: Claudio Fontana @ 2022-09-23 23:21 UTC (permalink / raw)
  To: Paolo Bonzini, Richard Henderson, Markus Armbruster, Kevin Wolf
  Cc: qemu-devel, dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P . Berrangé, Philippe Mathieu-Daudé,
	Claudio Fontana

From: Kevin Wolf <kwolf@redhat.com>

Signed-off-by: Claudio Fontana <cfontana@suse.de>
---
 block/dmg.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/block/dmg.c b/block/dmg.c
index a422cf8d5b..f54f1d99d4 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -254,6 +254,25 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds,
     for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) {
         s->types[i] = buff_read_uint32(buffer, offset);
         if (!dmg_is_known_block_type(s->types[i])) {
+            switch (s->types[i]) {
+            case UDBZ:
+                warn_report_once("dmg-bzip2 module is missing, accessing bzip2 "
+                                 "compressed blocks will result in I/O errors");
+                break;
+            case ULFO:
+                warn_report_once("dmg-lzfse module is missing, accessing lzfse "
+                                 "compressed blocks will result in I/O errors");
+                break;
+            case UDCM:
+            case UDLE:
+                /* Comments and last entry can be ignored without problems */
+                break;
+            default:
+                warn_report_once("Image contains chunks of unknown type %x, "
+                                 "accessing them will result in I/O errors",
+                                 s->types[i]);
+                break;
+            }
             chunk_count--;
             i--;
             offset += 40;
-- 
2.26.2



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

* [PATCH v6 5/5] accel: abort if we fail to load the accelerator plugin
  2022-09-23 23:20 [PATCH v6 0/5] improve error handling for module load Claudio Fontana
                   ` (3 preceding siblings ...)
  2022-09-23 23:21 ` [PATCH v6 4/5] dmg: warn when opening dmg images containing blocks of unknown type Claudio Fontana
@ 2022-09-23 23:21 ` Claudio Fontana
  2022-09-24 12:35   ` Philippe Mathieu-Daudé via
  4 siblings, 1 reply; 18+ messages in thread
From: Claudio Fontana @ 2022-09-23 23:21 UTC (permalink / raw)
  To: Paolo Bonzini, Richard Henderson, Markus Armbruster, Kevin Wolf
  Cc: qemu-devel, dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P . Berrangé, Philippe Mathieu-Daudé,
	Claudio Fontana

if QEMU is configured with modules enabled, it is possible that the
load of an accelerator module will fail.
Abort in this case, relying on module_object_class_by_name to report
the specific load error if any.

Signed-off-by: Claudio Fontana <cfontana@suse.de>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
 accel/accel-softmmu.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/accel/accel-softmmu.c b/accel/accel-softmmu.c
index 67276e4f52..9fa4849f2c 100644
--- a/accel/accel-softmmu.c
+++ b/accel/accel-softmmu.c
@@ -66,6 +66,7 @@ void accel_init_ops_interfaces(AccelClass *ac)
 {
     const char *ac_name;
     char *ops_name;
+    ObjectClass *oc;
     AccelOpsClass *ops;
 
     ac_name = object_class_get_name(OBJECT_CLASS(ac));
@@ -73,8 +74,13 @@ void accel_init_ops_interfaces(AccelClass *ac)
 
     ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name);
     ops = ACCEL_OPS_CLASS(module_object_class_by_name(ops_name));
+    oc = module_object_class_by_name(ops_name);
+    if (!oc) {
+        error_report("fatal: could not load module for type '%s'", ops_name);
+        abort();
+    }
     g_free(ops_name);
-
+    ops = ACCEL_OPS_CLASS(oc);
     /*
      * all accelerators need to define ops, providing at least a mandatory
      * non-NULL create_vcpu_thread operation.
-- 
2.26.2



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

* Re: [PATCH v6 5/5] accel: abort if we fail to load the accelerator plugin
  2022-09-23 23:21 ` [PATCH v6 5/5] accel: abort if we fail to load the accelerator plugin Claudio Fontana
@ 2022-09-24 12:35   ` Philippe Mathieu-Daudé via
  2022-09-26  7:58     ` Claudio Fontana
  0 siblings, 1 reply; 18+ messages in thread
From: Philippe Mathieu-Daudé via @ 2022-09-24 12:35 UTC (permalink / raw)
  To: Claudio Fontana, Paolo Bonzini, Richard Henderson,
	Markus Armbruster, Kevin Wolf
  Cc: qemu-devel, dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P . Berrangé

On 24/9/22 01:21, Claudio Fontana wrote:
> if QEMU is configured with modules enabled, it is possible that the
> load of an accelerator module will fail.
> Abort in this case, relying on module_object_class_by_name to report
> the specific load error if any.
> 
> Signed-off-by: Claudio Fontana <cfontana@suse.de>
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>   accel/accel-softmmu.c | 8 +++++++-
>   1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/accel/accel-softmmu.c b/accel/accel-softmmu.c
> index 67276e4f52..9fa4849f2c 100644
> --- a/accel/accel-softmmu.c
> +++ b/accel/accel-softmmu.c
> @@ -66,6 +66,7 @@ void accel_init_ops_interfaces(AccelClass *ac)
>   {
>       const char *ac_name;
>       char *ops_name;
> +    ObjectClass *oc;
>       AccelOpsClass *ops;
>   
>       ac_name = object_class_get_name(OBJECT_CLASS(ac));
> @@ -73,8 +74,13 @@ void accel_init_ops_interfaces(AccelClass *ac)
>   
>       ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name);
>       ops = ACCEL_OPS_CLASS(module_object_class_by_name(ops_name));
> +    oc = module_object_class_by_name(ops_name);
> +    if (!oc) {
> +        error_report("fatal: could not load module for type '%s'", ops_name);
> +        abort();

I still think a coredump won't help at all to figure the problem here: a 
module is missing, we know its name. Anyhow I don't mind much, and this
can be cleaned later, so:

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +    }
>       g_free(ops_name);
> -
> +    ops = ACCEL_OPS_CLASS(oc);
>       /*
>        * all accelerators need to define ops, providing at least a mandatory
>        * non-NULL create_vcpu_thread operation.



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

* Re: [PATCH v6 5/5] accel: abort if we fail to load the accelerator plugin
  2022-09-24 12:35   ` Philippe Mathieu-Daudé via
@ 2022-09-26  7:58     ` Claudio Fontana
  2022-09-26 10:56       ` Kevin Wolf
  0 siblings, 1 reply; 18+ messages in thread
From: Claudio Fontana @ 2022-09-26  7:58 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé,
	Paolo Bonzini, Richard Henderson, Markus Armbruster, Kevin Wolf
  Cc: qemu-devel, dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P . Berrangé

On 9/24/22 14:35, Philippe Mathieu-Daudé via wrote:
> On 24/9/22 01:21, Claudio Fontana wrote:
>> if QEMU is configured with modules enabled, it is possible that the
>> load of an accelerator module will fail.
>> Abort in this case, relying on module_object_class_by_name to report
>> the specific load error if any.
>>
>> Signed-off-by: Claudio Fontana <cfontana@suse.de>
>> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
>> ---
>>   accel/accel-softmmu.c | 8 +++++++-
>>   1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/accel/accel-softmmu.c b/accel/accel-softmmu.c
>> index 67276e4f52..9fa4849f2c 100644
>> --- a/accel/accel-softmmu.c
>> +++ b/accel/accel-softmmu.c
>> @@ -66,6 +66,7 @@ void accel_init_ops_interfaces(AccelClass *ac)
>>   {
>>       const char *ac_name;
>>       char *ops_name;
>> +    ObjectClass *oc;
>>       AccelOpsClass *ops;
>>   
>>       ac_name = object_class_get_name(OBJECT_CLASS(ac));
>> @@ -73,8 +74,13 @@ void accel_init_ops_interfaces(AccelClass *ac)
>>   
>>       ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name);
>>       ops = ACCEL_OPS_CLASS(module_object_class_by_name(ops_name));
>> +    oc = module_object_class_by_name(ops_name);
>> +    if (!oc) {
>> +        error_report("fatal: could not load module for type '%s'", ops_name);
>> +        abort();
> 
> I still think a coredump won't help at all to figure the problem here: a 

I can change this from abort to exit(1), the issue I am seeing is, usually when we fail to create or initialize objects
we seem to be using abort(), the most prominent examples are in qom/object.c:

static TypeImpl *type_new(const TypeInfo *info)
{
    TypeImpl *ti = g_malloc0(sizeof(*ti));
    int i;

    g_assert(info->name != NULL);

    if (type_table_lookup(info->name) != NULL) {
        fprintf(stderr, "Registering `%s' which already exists\n", info->name);
        abort();
    }

...

void object_initialize(void *data, size_t size, const char *typename)
{
    TypeImpl *type = type_get_by_name(typename);

#ifdef CONFIG_MODULES
    if (!type) {
        Error *local_err = NULL;
        int rv = module_load_qom(typename, &local_err);
        if (rv > 0) {
            type = type_get_by_name(typename);
        } else if (rv < 0) {
            error_report_err(local_err);
        }
    }
#endif
    if (!type) {
        error_report("missing object type '%s'", typename);
        abort();
    }

    object_initialize_with_type(data, size, type);
}


Do you propose to change only the assert in accel_init_ops_interfaces to exit(1)?

Or the other case as well in the series? (ie hw/core/qdev.c qdev_new() ?)

Do you propose to change this consistently through the codebase including the object.c snippets above?


> module is missing, we know its name. Anyhow I don't mind much, and this
> can be cleaned later, so:

Sure this could be fixed later with a series that tries to use exit() vs abort() consistently throughout the codebase when initializing and creating objects.

> 
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> 

Thanks!

Claudio

>> +    }
>>       g_free(ops_name);
>> -
>> +    ops = ACCEL_OPS_CLASS(oc);
>>       /*
>>        * all accelerators need to define ops, providing at least a mandatory
>>        * non-NULL create_vcpu_thread operation.
> 
> 



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

* Re: [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom
  2022-09-23 23:21 ` [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom Claudio Fontana
@ 2022-09-26 10:38   ` Kevin Wolf
  2022-09-26 13:28     ` Claudio Fontana
  2022-09-26 13:54     ` Claudio Fontana
  0 siblings, 2 replies; 18+ messages in thread
From: Kevin Wolf @ 2022-09-26 10:38 UTC (permalink / raw)
  To: Claudio Fontana
  Cc: Paolo Bonzini, Richard Henderson, Markus Armbruster, qemu-devel,
	dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P . Berrangé, Philippe Mathieu-Daudé

Am 24.09.2022 um 01:21 hat Claudio Fontana geschrieben:
> improve error handling during module load, by changing:
> 
> bool module_load(const char *prefix, const char *lib_name);
> void module_load_qom(const char *type);
> 
> to:
> 
> int module_load(const char *prefix, const char *name, Error **errp);
> int module_load_qom(const char *type, Error **errp);
> 
> where the return value is:
> 
>  -1 on module load error, and errp is set with the error
>   0 on module or one of its dependencies are not installed
>   1 on module load success
>   2 on module load success (module already loaded or built-in)
> 
> module_load_qom_one has been introduced in:
> 
> commit 28457744c345 ("module: qom module support"), which built on top of
> module_load_one, but discarded the bool return value. Restore it.
> 
> Adapt all callers to emit errors, or ignore them, or fail hard,
> as appropriate in each context.
> 
> Some memory leaks also fixed as part of the module_load changes.
> 
> audio: when attempting to load an audio module, report module load errors.
> block: when attempting to load a block module, report module load errors.
> console: when attempting to load a display module, report module load errors.
> 
> qdev: when creating a new qdev Device object (DeviceState), report load errors.
>       If a module cannot be loaded to create that device, now abort execution.
> 
> qom/object.c: when initializing a QOM object, or looking up class_by_name,
>               report module load errors.
> 
> qtest: when processing the "module_load" qtest command, report errors
>        in the load of the module.
> 
> Signed-off-by: Claudio Fontana <cfontana@suse.de>

> diff --git a/block/dmg.c b/block/dmg.c
> index 007b8d9996..a422cf8d5b 100644
> --- a/block/dmg.c
> +++ b/block/dmg.c
> @@ -434,6 +434,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>      uint64_t plist_xml_offset, plist_xml_length;
>      int64_t offset;
>      int ret;
> +    Error *local_err = NULL;
>  
>      ret = bdrv_apply_auto_read_only(bs, NULL, errp);
>      if (ret < 0) {
> @@ -446,8 +447,15 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>          return -EINVAL;
>      }
>  
> -    block_module_load("dmg-bz2");
> -    block_module_load("dmg-lzfse");
> +    if (block_module_load("dmg-bz2", &local_err) < 0) {
> +        error_report_err(local_err);
> +        return -EINVAL;
> +    }
> +    local_err = NULL;
> +    if (block_module_load("dmg-lzfse", &local_err) < 0) {
> +        error_report_err(local_err);
> +        return -EINVAL;
> +    }

Why don't we pass the existing errp instead of adding a new local_err
and printing it locally? If we use error_report_err(local_err) and leave
errp untouched, the caller will print another less specific error
message, which is generally not what we want.

Kevin



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

* Re: [PATCH v6 5/5] accel: abort if we fail to load the accelerator plugin
  2022-09-26  7:58     ` Claudio Fontana
@ 2022-09-26 10:56       ` Kevin Wolf
  2022-09-26 11:21         ` Claudio Fontana
  0 siblings, 1 reply; 18+ messages in thread
From: Kevin Wolf @ 2022-09-26 10:56 UTC (permalink / raw)
  To: Claudio Fontana
  Cc: Philippe Mathieu-Daudé,
	Paolo Bonzini, Richard Henderson, Markus Armbruster, qemu-devel,
	dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P . Berrangé

Am 26.09.2022 um 09:58 hat Claudio Fontana geschrieben:
> On 9/24/22 14:35, Philippe Mathieu-Daudé via wrote:
> > On 24/9/22 01:21, Claudio Fontana wrote:
> >> if QEMU is configured with modules enabled, it is possible that the
> >> load of an accelerator module will fail.
> >> Abort in this case, relying on module_object_class_by_name to report
> >> the specific load error if any.
> >>
> >> Signed-off-by: Claudio Fontana <cfontana@suse.de>
> >> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> >> ---
> >>   accel/accel-softmmu.c | 8 +++++++-
> >>   1 file changed, 7 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/accel/accel-softmmu.c b/accel/accel-softmmu.c
> >> index 67276e4f52..9fa4849f2c 100644
> >> --- a/accel/accel-softmmu.c
> >> +++ b/accel/accel-softmmu.c
> >> @@ -66,6 +66,7 @@ void accel_init_ops_interfaces(AccelClass *ac)
> >>   {
> >>       const char *ac_name;
> >>       char *ops_name;
> >> +    ObjectClass *oc;
> >>       AccelOpsClass *ops;
> >>   
> >>       ac_name = object_class_get_name(OBJECT_CLASS(ac));
> >> @@ -73,8 +74,13 @@ void accel_init_ops_interfaces(AccelClass *ac)
> >>   
> >>       ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name);
> >>       ops = ACCEL_OPS_CLASS(module_object_class_by_name(ops_name));
> >> +    oc = module_object_class_by_name(ops_name);
> >> +    if (!oc) {
> >> +        error_report("fatal: could not load module for type '%s'", ops_name);
> >> +        abort();
> > 
> > I still think a coredump won't help at all to figure the problem here: a 
> 
> I can change this from abort to exit(1), the issue I am seeing is, usually when we fail to create or initialize objects
> we seem to be using abort(), the most prominent examples are in qom/object.c:
> 
> static TypeImpl *type_new(const TypeInfo *info)
> {
>     TypeImpl *ti = g_malloc0(sizeof(*ti));
>     int i;
> 
>     g_assert(info->name != NULL);
> 
>     if (type_table_lookup(info->name) != NULL) {
>         fprintf(stderr, "Registering `%s' which already exists\n", info->name);
>         abort();
>     }
> 
> ...
> 
> void object_initialize(void *data, size_t size, const char *typename)
> {
>     TypeImpl *type = type_get_by_name(typename);
> 
> #ifdef CONFIG_MODULES
>     if (!type) {
>         Error *local_err = NULL;
>         int rv = module_load_qom(typename, &local_err);
>         if (rv > 0) {
>             type = type_get_by_name(typename);
>         } else if (rv < 0) {
>             error_report_err(local_err);
>         }
>     }
> #endif
>     if (!type) {
>         error_report("missing object type '%s'", typename);
>         abort();
>     }
> 
>     object_initialize_with_type(data, size, type);
> }
> 
> 
> Do you propose to change only the assert in accel_init_ops_interfaces
> to exit(1)?
> 
> Or the other case as well in the series? (ie hw/core/qdev.c qdev_new()
> ?)
> 
> Do you propose to change this consistently through the codebase
> including the object.c snippets above?

The difference with the snippets above (in the non-module case) is that
calling object_new() with a type that doesn't exist is a bug, it's an
programming error. Calling type_new() twice for the same TypeInfo or for
two TypeInfos with the same name is a programming error, too. abort() is
correct for situations that should never happen in a bug free QEMU.

Not being able to load a module is generally not a bug in QEMU, it's an
error of external origin. So here abort() is not appropriate.

The CONFIG_MODULES code in object_initialize() is problematic because it
doesn't have a way to deal with an error case that can happen without a
bug in QEMU. Without changing the prototype of the function to actually
allow error returns (which I suspect might be a very invasive change),
maybe the best approach is to just make it a fatal error and leave the
code mostly as it is in current master:

#ifdef CONFIG_MODULES
    if (!type) {
        /* Assuming that module_load_qom_one() returns an error if the
         * module doesn't exist */
        module_load_qom_one(typename, &error_fatal);
        type = type_get_by_name(typename);
    }
#endif
    if (!type) {
        error_report("missing object type '%s'", typename);
        abort();
    }

    object_initialize_with_type(data, size, type);

This makes it print an error message and exit(). Which is honestly not
great during runtime because it doesn't properly shut down QEMU, let
alone just fail the operation and keep running, but at least slightly
better than abort().

> > module is missing, we know its name. Anyhow I don't mind much, and this
> > can be cleaned later, so:
> 
> Sure this could be fixed later with a series that tries to use exit()
> vs abort() consistently throughout the codebase when initializing and
> creating objects.

This should mean consistently distinguishing programming errors (i.e.
QEMU bugs) from errors of external origin.

Kevin



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

* Re: [PATCH v6 5/5] accel: abort if we fail to load the accelerator plugin
  2022-09-26 10:56       ` Kevin Wolf
@ 2022-09-26 11:21         ` Claudio Fontana
  0 siblings, 0 replies; 18+ messages in thread
From: Claudio Fontana @ 2022-09-26 11:21 UTC (permalink / raw)
  To: Kevin Wolf
  Cc: Philippe Mathieu-Daudé,
	Paolo Bonzini, Richard Henderson, Markus Armbruster, qemu-devel,
	dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P. Berrangé

On 9/26/22 12:56, Kevin Wolf wrote:
> Am 26.09.2022 um 09:58 hat Claudio Fontana geschrieben:
>> On 9/24/22 14:35, Philippe Mathieu-Daudé via wrote:
>>> On 24/9/22 01:21, Claudio Fontana wrote:
>>>> if QEMU is configured with modules enabled, it is possible that the
>>>> load of an accelerator module will fail.
>>>> Abort in this case, relying on module_object_class_by_name to report
>>>> the specific load error if any.
>>>>
>>>> Signed-off-by: Claudio Fontana <cfontana@suse.de>
>>>> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
>>>> ---
>>>>   accel/accel-softmmu.c | 8 +++++++-
>>>>   1 file changed, 7 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/accel/accel-softmmu.c b/accel/accel-softmmu.c
>>>> index 67276e4f52..9fa4849f2c 100644
>>>> --- a/accel/accel-softmmu.c
>>>> +++ b/accel/accel-softmmu.c
>>>> @@ -66,6 +66,7 @@ void accel_init_ops_interfaces(AccelClass *ac)
>>>>   {
>>>>       const char *ac_name;
>>>>       char *ops_name;
>>>> +    ObjectClass *oc;
>>>>       AccelOpsClass *ops;
>>>>   
>>>>       ac_name = object_class_get_name(OBJECT_CLASS(ac));
>>>> @@ -73,8 +74,13 @@ void accel_init_ops_interfaces(AccelClass *ac)
>>>>   
>>>>       ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name);
>>>>       ops = ACCEL_OPS_CLASS(module_object_class_by_name(ops_name));
>>>> +    oc = module_object_class_by_name(ops_name);
>>>> +    if (!oc) {
>>>> +        error_report("fatal: could not load module for type '%s'", ops_name);
>>>> +        abort();
>>>
>>> I still think a coredump won't help at all to figure the problem here: a 
>>
>> I can change this from abort to exit(1), the issue I am seeing is, usually when we fail to create or initialize objects
>> we seem to be using abort(), the most prominent examples are in qom/object.c:
>>
>> static TypeImpl *type_new(const TypeInfo *info)
>> {
>>     TypeImpl *ti = g_malloc0(sizeof(*ti));
>>     int i;
>>
>>     g_assert(info->name != NULL);
>>
>>     if (type_table_lookup(info->name) != NULL) {
>>         fprintf(stderr, "Registering `%s' which already exists\n", info->name);
>>         abort();
>>     }
>>
>> ...
>>
>> void object_initialize(void *data, size_t size, const char *typename)
>> {
>>     TypeImpl *type = type_get_by_name(typename);
>>
>> #ifdef CONFIG_MODULES
>>     if (!type) {
>>         Error *local_err = NULL;
>>         int rv = module_load_qom(typename, &local_err);
>>         if (rv > 0) {
>>             type = type_get_by_name(typename);
>>         } else if (rv < 0) {
>>             error_report_err(local_err);
>>         }
>>     }
>> #endif
>>     if (!type) {
>>         error_report("missing object type '%s'", typename);
>>         abort();
>>     }
>>
>>     object_initialize_with_type(data, size, type);
>> }
>>
>>
>> Do you propose to change only the assert in accel_init_ops_interfaces
>> to exit(1)?
>>
>> Or the other case as well in the series? (ie hw/core/qdev.c qdev_new()
>> ?)
>>
>> Do you propose to change this consistently through the codebase
>> including the object.c snippets above?
> 
> The difference with the snippets above (in the non-module case) is that
> calling object_new() with a type that doesn't exist is a bug, it's an
I see where you are going, however in the object initialization case,
we still have the possibility that the type is available only after module load.

So according to your differentiation, we would have to exit() in the module case, and abort() if modules are disabled.


> programming error. Calling type_new() twice for the same TypeInfo or for
> two TypeInfos with the same name is a programming error, too. abort() is
> correct for situations that should never happen in a bug free QEMU.
> 
> Not being able to load a module is generally not a bug in QEMU, it's an
> error of external origin. So here abort() is not appropriate.
> 
> The CONFIG_MODULES code in object_initialize() is problematic because it
> doesn't have a way to deal with an error case that can happen without a
> bug in QEMU. Without changing the prototype of the function to actually
> allow error returns (which I suspect might be a very invasive change),
> maybe the best approach is to just make it a fatal error and leave the
> code mostly as it is in current master:
> 
> #ifdef CONFIG_MODULES
>     if (!type) {
>         /* Assuming that module_load_qom_one() returns an error if the
>          * module doesn't exist */
>         module_load_qom_one(typename, &error_fatal);
>         type = type_get_by_name(typename);
>     }
> #endif
>     if (!type) {
>         error_report("missing object type '%s'", typename);
>         abort();
>     }
> 
>     object_initialize_with_type(data, size, type);
> 
> This makes it print an error message and exit(). Which is honestly not
> great during runtime because it doesn't properly shut down QEMU, let
> alone just fail the operation and keep running, but at least slightly
> better than abort().

Ok so the core of the issue is, if CONFIG_MODULES we want to exit() and not abort() if the type is not available.

> 
>>> module is missing, we know its name. Anyhow I don't mind much, and this
>>> can be cleaned later, so:
>>
>> Sure this could be fixed later with a series that tries to use exit()
>> vs abort() consistently throughout the codebase when initializing and
>> creating objects.
> 
> This should mean consistently distinguishing programming errors (i.e.
> QEMU bugs) from errors of external origin.
> 
> Kevin
> 

Thanks,

I'll have a new pass with this in mind.

Claudio


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

* Re: [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom
  2022-09-26 10:38   ` Kevin Wolf
@ 2022-09-26 13:28     ` Claudio Fontana
  2022-09-26 13:54     ` Claudio Fontana
  1 sibling, 0 replies; 18+ messages in thread
From: Claudio Fontana @ 2022-09-26 13:28 UTC (permalink / raw)
  To: Kevin Wolf
  Cc: Paolo Bonzini, Richard Henderson, Markus Armbruster, qemu-devel,
	dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P. Berrangé, Philippe Mathieu-Daudé

On 9/26/22 12:38, Kevin Wolf wrote:
> Am 24.09.2022 um 01:21 hat Claudio Fontana geschrieben:
>> improve error handling during module load, by changing:
>>
>> bool module_load(const char *prefix, const char *lib_name);
>> void module_load_qom(const char *type);
>>
>> to:
>>
>> int module_load(const char *prefix, const char *name, Error **errp);
>> int module_load_qom(const char *type, Error **errp);
>>
>> where the return value is:
>>
>>  -1 on module load error, and errp is set with the error
>>   0 on module or one of its dependencies are not installed
>>   1 on module load success
>>   2 on module load success (module already loaded or built-in)
>>
>> module_load_qom_one has been introduced in:
>>
>> commit 28457744c345 ("module: qom module support"), which built on top of
>> module_load_one, but discarded the bool return value. Restore it.
>>
>> Adapt all callers to emit errors, or ignore them, or fail hard,
>> as appropriate in each context.
>>
>> Some memory leaks also fixed as part of the module_load changes.
>>
>> audio: when attempting to load an audio module, report module load errors.
>> block: when attempting to load a block module, report module load errors.
>> console: when attempting to load a display module, report module load errors.
>>
>> qdev: when creating a new qdev Device object (DeviceState), report load errors.
>>       If a module cannot be loaded to create that device, now abort execution.
>>
>> qom/object.c: when initializing a QOM object, or looking up class_by_name,
>>               report module load errors.
>>
>> qtest: when processing the "module_load" qtest command, report errors
>>        in the load of the module.
>>
>> Signed-off-by: Claudio Fontana <cfontana@suse.de>
> 
>> diff --git a/block/dmg.c b/block/dmg.c
>> index 007b8d9996..a422cf8d5b 100644
>> --- a/block/dmg.c
>> +++ b/block/dmg.c
>> @@ -434,6 +434,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>>      uint64_t plist_xml_offset, plist_xml_length;
>>      int64_t offset;
>>      int ret;
>> +    Error *local_err = NULL;
>>  
>>      ret = bdrv_apply_auto_read_only(bs, NULL, errp);
>>      if (ret < 0) {
>> @@ -446,8 +447,15 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>>          return -EINVAL;
>>      }
>>  
>> -    block_module_load("dmg-bz2");
>> -    block_module_load("dmg-lzfse");
>> +    if (block_module_load("dmg-bz2", &local_err) < 0) {
>> +        error_report_err(local_err);
>> +        return -EINVAL;
>> +    }
>> +    local_err = NULL;
>> +    if (block_module_load("dmg-lzfse", &local_err) < 0) {
>> +        error_report_err(local_err);
>> +        return -EINVAL;
>> +    }
> 
> Why don't we pass the existing errp instead of adding a new local_err
> and printing it locally? If we use error_report_err(local_err) and leave
> errp untouched, the caller will print another less specific error
> message, which is generally not what we want.
> 
> Kevin
> 

Ok, you know that code best, will merge the two.

Thanks,

Claudio 


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

* Re: [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom
  2022-09-26 10:38   ` Kevin Wolf
  2022-09-26 13:28     ` Claudio Fontana
@ 2022-09-26 13:54     ` Claudio Fontana
  2022-09-27  7:54       ` Markus Armbruster
  1 sibling, 1 reply; 18+ messages in thread
From: Claudio Fontana @ 2022-09-26 13:54 UTC (permalink / raw)
  To: Kevin Wolf
  Cc: Paolo Bonzini, Richard Henderson, Markus Armbruster, qemu-devel,
	dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P. Berrangé, Philippe Mathieu-Daudé

On 9/26/22 12:38, Kevin Wolf wrote:
> Am 24.09.2022 um 01:21 hat Claudio Fontana geschrieben:
>> improve error handling during module load, by changing:
>>
>> bool module_load(const char *prefix, const char *lib_name);
>> void module_load_qom(const char *type);
>>
>> to:
>>
>> int module_load(const char *prefix, const char *name, Error **errp);
>> int module_load_qom(const char *type, Error **errp);
>>
>> where the return value is:
>>
>>  -1 on module load error, and errp is set with the error
>>   0 on module or one of its dependencies are not installed
>>   1 on module load success
>>   2 on module load success (module already loaded or built-in)
>>
>> module_load_qom_one has been introduced in:
>>
>> commit 28457744c345 ("module: qom module support"), which built on top of
>> module_load_one, but discarded the bool return value. Restore it.
>>
>> Adapt all callers to emit errors, or ignore them, or fail hard,
>> as appropriate in each context.
>>
>> Some memory leaks also fixed as part of the module_load changes.
>>
>> audio: when attempting to load an audio module, report module load errors.
>> block: when attempting to load a block module, report module load errors.
>> console: when attempting to load a display module, report module load errors.
>>
>> qdev: when creating a new qdev Device object (DeviceState), report load errors.
>>       If a module cannot be loaded to create that device, now abort execution.
>>
>> qom/object.c: when initializing a QOM object, or looking up class_by_name,
>>               report module load errors.
>>
>> qtest: when processing the "module_load" qtest command, report errors
>>        in the load of the module.
>>
>> Signed-off-by: Claudio Fontana <cfontana@suse.de>
> 
>> diff --git a/block/dmg.c b/block/dmg.c
>> index 007b8d9996..a422cf8d5b 100644
>> --- a/block/dmg.c
>> +++ b/block/dmg.c
>> @@ -434,6 +434,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>>      uint64_t plist_xml_offset, plist_xml_length;
>>      int64_t offset;
>>      int ret;
>> +    Error *local_err = NULL;
>>  
>>      ret = bdrv_apply_auto_read_only(bs, NULL, errp);
>>      if (ret < 0) {
>> @@ -446,8 +447,15 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>>          return -EINVAL;
>>      }
>>  
>> -    block_module_load("dmg-bz2");
>> -    block_module_load("dmg-lzfse");
>> +    if (block_module_load("dmg-bz2", &local_err) < 0) {
>> +        error_report_err(local_err);
>> +        return -EINVAL;
>> +    }
>> +    local_err = NULL;
>> +    if (block_module_load("dmg-lzfse", &local_err) < 0) {
>> +        error_report_err(local_err);
>> +        return -EINVAL;

I am concerned about the resources allocation here though,
is returning EINVAL here right, vs using "goto fail"?

I matched the behavior of the preceding call:

    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
                               BDRV_CHILD_IMAGE, false, errp);
    if (!bs->file) {
        return -EINVAL;
    }

But afterwards the code goes:
.
    /* locate the UDIF trailer */
    offset = dmg_find_koly_offset(bs->file, errp);
    if (offset < 0) {
        ret = offset;
        goto fail;
    }

Should the resources be freed or not in your view?

Thanks,

Claudio



>> +    }
> 
> Why don't we pass the existing errp instead of adding a new local_err
> and printing it locally? If we use error_report_err(local_err) and leave
> errp untouched, the caller will print another less specific error
> message, which is generally not what we want.
> 
> Kevin
> 



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

* Re: [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom
  2022-09-26 13:54     ` Claudio Fontana
@ 2022-09-27  7:54       ` Markus Armbruster
  2022-09-27  9:13         ` Claudio Fontana
  2022-09-27 11:57         ` Kevin Wolf
  0 siblings, 2 replies; 18+ messages in thread
From: Markus Armbruster @ 2022-09-27  7:54 UTC (permalink / raw)
  To: Claudio Fontana
  Cc: Kevin Wolf, Paolo Bonzini, Richard Henderson, qemu-devel,
	dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P. Berrangé, Philippe Mathieu-Daudé

Claudio Fontana <cfontana@suse.de> writes:

> On 9/26/22 12:38, Kevin Wolf wrote:
>> Am 24.09.2022 um 01:21 hat Claudio Fontana geschrieben:
>>> improve error handling during module load, by changing:
>>>
>>> bool module_load(const char *prefix, const char *lib_name);
>>> void module_load_qom(const char *type);
>>>
>>> to:
>>>
>>> int module_load(const char *prefix, const char *name, Error **errp);
>>> int module_load_qom(const char *type, Error **errp);
>>>
>>> where the return value is:
>>>
>>>  -1 on module load error, and errp is set with the error
>>>   0 on module or one of its dependencies are not installed
>>>   1 on module load success
>>>   2 on module load success (module already loaded or built-in)
>>>
>>> module_load_qom_one has been introduced in:
>>>
>>> commit 28457744c345 ("module: qom module support"), which built on top of
>>> module_load_one, but discarded the bool return value. Restore it.
>>>
>>> Adapt all callers to emit errors, or ignore them, or fail hard,
>>> as appropriate in each context.
>>>
>>> Some memory leaks also fixed as part of the module_load changes.
>>>
>>> audio: when attempting to load an audio module, report module load errors.
>>> block: when attempting to load a block module, report module load errors.
>>> console: when attempting to load a display module, report module load errors.
>>>
>>> qdev: when creating a new qdev Device object (DeviceState), report load errors.
>>>       If a module cannot be loaded to create that device, now abort execution.
>>>
>>> qom/object.c: when initializing a QOM object, or looking up class_by_name,
>>>               report module load errors.
>>>
>>> qtest: when processing the "module_load" qtest command, report errors
>>>        in the load of the module.
>>>
>>> Signed-off-by: Claudio Fontana <cfontana@suse.de>
>> 
>>> diff --git a/block/dmg.c b/block/dmg.c
>>> index 007b8d9996..a422cf8d5b 100644
>>> --- a/block/dmg.c
>>> +++ b/block/dmg.c
>>> @@ -434,6 +434,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>>>      uint64_t plist_xml_offset, plist_xml_length;
>>>      int64_t offset;
>>>      int ret;
>>> +    Error *local_err = NULL;
>>>  
>>>      ret = bdrv_apply_auto_read_only(bs, NULL, errp);
>>>      if (ret < 0) {
>>> @@ -446,8 +447,15 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>>>          return -EINVAL;
>>>      }
>>>  
>>> -    block_module_load("dmg-bz2");
>>> -    block_module_load("dmg-lzfse");
>>> +    if (block_module_load("dmg-bz2", &local_err) < 0) {
>>> +        error_report_err(local_err);
>>> +        return -EINVAL;
>>> +    }
>>> +    local_err = NULL;
>>> +    if (block_module_load("dmg-lzfse", &local_err) < 0) {
>>> +        error_report_err(local_err);
>>> +        return -EINVAL;
>
> I am concerned about the resources allocation here though,
> is returning EINVAL here right, vs using "goto fail"?
>
> I matched the behavior of the preceding call:
>
>     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
>                                BDRV_CHILD_IMAGE, false, errp);
>     if (!bs->file) {
>         return -EINVAL;
>     }
>
> But afterwards the code goes:
> .
>     /* locate the UDIF trailer */
>     offset = dmg_find_koly_offset(bs->file, errp);
>     if (offset < 0) {
>         ret = offset;
>         goto fail;
>     }
>
> Should the resources be freed or not in your view?

Functions should generally fail cleanly, and that means undoing side
effects such as allocations.

Typically, we undo in reverse order, and goto the right spot in that
sequence.

When the undo can be made to work whether the "do" happened or not, we
can use fewer labels for simplicity.  For instance, g_free(mumble) works
as long as mumble is initialized to NULL.

In this function:

   fail:
       g_free(s->types);
       g_free(s->offsets);
       g_free(s->lengths);
       g_free(s->sectors);
       g_free(s->sectorcounts);
       qemu_vfree(s->compressed_chunk);
       qemu_vfree(s->uncompressed_chunk);
       return ret;

I figure this undoes side effects hidden in the read functions called.

Potential issue before this patch: I can't see bdrv_open_child() being
undone.  Shouldn't we close bs->file?  And what about
bdrv_open_child()'s side effect on @options?

[...]



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

* Re: [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom
  2022-09-27  7:54       ` Markus Armbruster
@ 2022-09-27  9:13         ` Claudio Fontana
  2022-09-27 11:53           ` Kevin Wolf
  2022-09-27 11:57         ` Kevin Wolf
  1 sibling, 1 reply; 18+ messages in thread
From: Claudio Fontana @ 2022-09-27  9:13 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Paolo Bonzini, Richard Henderson, qemu-devel,
	dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P. Berrangé, Philippe Mathieu-Daudé

On 9/27/22 09:54, Markus Armbruster wrote:
> Claudio Fontana <cfontana@suse.de> writes:
> 
>> On 9/26/22 12:38, Kevin Wolf wrote:
>>> Am 24.09.2022 um 01:21 hat Claudio Fontana geschrieben:
>>>> improve error handling during module load, by changing:
>>>>
>>>> bool module_load(const char *prefix, const char *lib_name);
>>>> void module_load_qom(const char *type);
>>>>
>>>> to:
>>>>
>>>> int module_load(const char *prefix, const char *name, Error **errp);
>>>> int module_load_qom(const char *type, Error **errp);
>>>>
>>>> where the return value is:
>>>>
>>>>  -1 on module load error, and errp is set with the error
>>>>   0 on module or one of its dependencies are not installed
>>>>   1 on module load success
>>>>   2 on module load success (module already loaded or built-in)
>>>>
>>>> module_load_qom_one has been introduced in:
>>>>
>>>> commit 28457744c345 ("module: qom module support"), which built on top of
>>>> module_load_one, but discarded the bool return value. Restore it.
>>>>
>>>> Adapt all callers to emit errors, or ignore them, or fail hard,
>>>> as appropriate in each context.
>>>>
>>>> Some memory leaks also fixed as part of the module_load changes.
>>>>
>>>> audio: when attempting to load an audio module, report module load errors.
>>>> block: when attempting to load a block module, report module load errors.
>>>> console: when attempting to load a display module, report module load errors.
>>>>
>>>> qdev: when creating a new qdev Device object (DeviceState), report load errors.
>>>>       If a module cannot be loaded to create that device, now abort execution.
>>>>
>>>> qom/object.c: when initializing a QOM object, or looking up class_by_name,
>>>>               report module load errors.
>>>>
>>>> qtest: when processing the "module_load" qtest command, report errors
>>>>        in the load of the module.
>>>>
>>>> Signed-off-by: Claudio Fontana <cfontana@suse.de>
>>>
>>>> diff --git a/block/dmg.c b/block/dmg.c
>>>> index 007b8d9996..a422cf8d5b 100644
>>>> --- a/block/dmg.c
>>>> +++ b/block/dmg.c
>>>> @@ -434,6 +434,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>>>>      uint64_t plist_xml_offset, plist_xml_length;
>>>>      int64_t offset;
>>>>      int ret;
>>>> +    Error *local_err = NULL;
>>>>  
>>>>      ret = bdrv_apply_auto_read_only(bs, NULL, errp);
>>>>      if (ret < 0) {
>>>> @@ -446,8 +447,15 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>>>>          return -EINVAL;
>>>>      }
>>>>  
>>>> -    block_module_load("dmg-bz2");
>>>> -    block_module_load("dmg-lzfse");
>>>> +    if (block_module_load("dmg-bz2", &local_err) < 0) {
>>>> +        error_report_err(local_err);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +    local_err = NULL;
>>>> +    if (block_module_load("dmg-lzfse", &local_err) < 0) {
>>>> +        error_report_err(local_err);
>>>> +        return -EINVAL;
>>
>> I am concerned about the resources allocation here though,
>> is returning EINVAL here right, vs using "goto fail"?
>>
>> I matched the behavior of the preceding call:
>>
>>     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
>>                                BDRV_CHILD_IMAGE, false, errp);
>>     if (!bs->file) {
>>         return -EINVAL;
>>     }
>>
>> But afterwards the code goes:
>> .
>>     /* locate the UDIF trailer */
>>     offset = dmg_find_koly_offset(bs->file, errp);
>>     if (offset < 0) {
>>         ret = offset;
>>         goto fail;
>>     }
>>
>> Should the resources be freed or not in your view?
> 
> Functions should generally fail cleanly, and that means undoing side
> effects such as allocations.

This is pretty obvious.

Note that this function does not perform the allocations,
bdrv_open is called with existing allocated resources,
and then frees those resources only in specific code paths.

I am asking here because the code preceding my hunk is doing:

"
    ret = bdrv_apply_auto_read_only(bs, NULL, errp);
    if (ret < 0) {
        return ret;
    }

    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
                               BDRV_CHILD_IMAGE, false, errp);
    if (!bs->file) {
        return -EINVAL;
    }
"

So something seems fishy here, looks inconsistent with the successive code that instead frees bs resources on error.

Might be correct, but would need looking deeper into this by block-aware people. Kevin?



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

* Re: [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom
  2022-09-27  9:13         ` Claudio Fontana
@ 2022-09-27 11:53           ` Kevin Wolf
  2022-09-27 12:54             ` Claudio Fontana
  0 siblings, 1 reply; 18+ messages in thread
From: Kevin Wolf @ 2022-09-27 11:53 UTC (permalink / raw)
  To: Claudio Fontana
  Cc: Markus Armbruster, Paolo Bonzini, Richard Henderson, qemu-devel,
	dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P. Berrangé, Philippe Mathieu-Daudé

Am 27.09.2022 um 11:13 hat Claudio Fontana geschrieben:
> On 9/27/22 09:54, Markus Armbruster wrote:
> > Claudio Fontana <cfontana@suse.de> writes:
> > 
> >> On 9/26/22 12:38, Kevin Wolf wrote:
> >>> Am 24.09.2022 um 01:21 hat Claudio Fontana geschrieben:
> >>>> improve error handling during module load, by changing:
> >>>>
> >>>> bool module_load(const char *prefix, const char *lib_name);
> >>>> void module_load_qom(const char *type);
> >>>>
> >>>> to:
> >>>>
> >>>> int module_load(const char *prefix, const char *name, Error **errp);
> >>>> int module_load_qom(const char *type, Error **errp);
> >>>>
> >>>> where the return value is:
> >>>>
> >>>>  -1 on module load error, and errp is set with the error
> >>>>   0 on module or one of its dependencies are not installed
> >>>>   1 on module load success
> >>>>   2 on module load success (module already loaded or built-in)
> >>>>
> >>>> module_load_qom_one has been introduced in:
> >>>>
> >>>> commit 28457744c345 ("module: qom module support"), which built on top of
> >>>> module_load_one, but discarded the bool return value. Restore it.
> >>>>
> >>>> Adapt all callers to emit errors, or ignore them, or fail hard,
> >>>> as appropriate in each context.
> >>>>
> >>>> Some memory leaks also fixed as part of the module_load changes.
> >>>>
> >>>> audio: when attempting to load an audio module, report module load errors.
> >>>> block: when attempting to load a block module, report module load errors.
> >>>> console: when attempting to load a display module, report module load errors.
> >>>>
> >>>> qdev: when creating a new qdev Device object (DeviceState), report load errors.
> >>>>       If a module cannot be loaded to create that device, now abort execution.
> >>>>
> >>>> qom/object.c: when initializing a QOM object, or looking up class_by_name,
> >>>>               report module load errors.
> >>>>
> >>>> qtest: when processing the "module_load" qtest command, report errors
> >>>>        in the load of the module.
> >>>>
> >>>> Signed-off-by: Claudio Fontana <cfontana@suse.de>
> >>>
> >>>> diff --git a/block/dmg.c b/block/dmg.c
> >>>> index 007b8d9996..a422cf8d5b 100644
> >>>> --- a/block/dmg.c
> >>>> +++ b/block/dmg.c
> >>>> @@ -434,6 +434,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
> >>>>      uint64_t plist_xml_offset, plist_xml_length;
> >>>>      int64_t offset;
> >>>>      int ret;
> >>>> +    Error *local_err = NULL;
> >>>>  
> >>>>      ret = bdrv_apply_auto_read_only(bs, NULL, errp);
> >>>>      if (ret < 0) {
> >>>> @@ -446,8 +447,15 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
> >>>>          return -EINVAL;
> >>>>      }
> >>>>  
> >>>> -    block_module_load("dmg-bz2");
> >>>> -    block_module_load("dmg-lzfse");
> >>>> +    if (block_module_load("dmg-bz2", &local_err) < 0) {
> >>>> +        error_report_err(local_err);
> >>>> +        return -EINVAL;
> >>>> +    }
> >>>> +    local_err = NULL;
> >>>> +    if (block_module_load("dmg-lzfse", &local_err) < 0) {
> >>>> +        error_report_err(local_err);
> >>>> +        return -EINVAL;
> >>
> >> I am concerned about the resources allocation here though,
> >> is returning EINVAL here right, vs using "goto fail"?
> >>
> >> I matched the behavior of the preceding call:
> >>
> >>     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
> >>                                BDRV_CHILD_IMAGE, false, errp);
> >>     if (!bs->file) {
> >>         return -EINVAL;
> >>     }
> >>
> >> But afterwards the code goes:
> >> .
> >>     /* locate the UDIF trailer */
> >>     offset = dmg_find_koly_offset(bs->file, errp);
> >>     if (offset < 0) {
> >>         ret = offset;
> >>         goto fail;
> >>     }
> >>
> >> Should the resources be freed or not in your view?
> > 
> > Functions should generally fail cleanly, and that means undoing side
> > effects such as allocations.
> 
> This is pretty obvious.
> 
> Note that this function does not perform the allocations,
> bdrv_open is called with existing allocated resources,
> and then frees those resources only in specific code paths.

These aren't allocated, the caller just allocates the BDRVDMGState and
initialises it with zeros. In other words, at this point, the free
functions don't do anything yet and you may or may not call them, it
doesn't matter.

We have an explicit line like this after the module loading:

    s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;

It's completely unnecessary because the BDRVDMGState is already
initialised like this, but if you think of this as the point where these
fields come to life, using a direct return before it and 'goto fail'
afterwards makes a little bit of sense at least.

As far as I can tell, only dmg_read_resource_fork() or
dmg_read_plist_xml() actually allocate some memeory that needs to be
undone, so some of the existing 'goto fail' instances could be direct
returns. But they're not wrong either, so no need to change them.

Kevin



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

* Re: [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom
  2022-09-27  7:54       ` Markus Armbruster
  2022-09-27  9:13         ` Claudio Fontana
@ 2022-09-27 11:57         ` Kevin Wolf
  1 sibling, 0 replies; 18+ messages in thread
From: Kevin Wolf @ 2022-09-27 11:57 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Claudio Fontana, Paolo Bonzini, Richard Henderson, qemu-devel,
	dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P. Berrangé, Philippe Mathieu-Daudé

Am 27.09.2022 um 09:54 hat Markus Armbruster geschrieben:
> Potential issue before this patch: I can't see bdrv_open_child() being
> undone.  Shouldn't we close bs->file?  And what about
> bdrv_open_child()'s side effect on @options?

bs->file is handled by the caller, bdrv_open_driver().

options is going to be freed anyway, so I don't think we care about its
content. And anyway, doesn't bdrv_open_child() only remove those entries
from it that it processed, like in the success case?

Kevin



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

* Re: [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom
  2022-09-27 11:53           ` Kevin Wolf
@ 2022-09-27 12:54             ` Claudio Fontana
  0 siblings, 0 replies; 18+ messages in thread
From: Claudio Fontana @ 2022-09-27 12:54 UTC (permalink / raw)
  To: Kevin Wolf
  Cc: Markus Armbruster, Paolo Bonzini, Richard Henderson, qemu-devel,
	dinechin, Gerd Hoffmann, Marc-André Lureau,
	Daniel P. Berrangé, Philippe Mathieu-Daudé

On 9/27/22 13:53, Kevin Wolf wrote:
> Am 27.09.2022 um 11:13 hat Claudio Fontana geschrieben:
>> On 9/27/22 09:54, Markus Armbruster wrote:
>>> Claudio Fontana <cfontana@suse.de> writes:
>>>
>>>> On 9/26/22 12:38, Kevin Wolf wrote:
>>>>> Am 24.09.2022 um 01:21 hat Claudio Fontana geschrieben:
>>>>>> improve error handling during module load, by changing:
>>>>>>
>>>>>> bool module_load(const char *prefix, const char *lib_name);
>>>>>> void module_load_qom(const char *type);
>>>>>>
>>>>>> to:
>>>>>>
>>>>>> int module_load(const char *prefix, const char *name, Error **errp);
>>>>>> int module_load_qom(const char *type, Error **errp);
>>>>>>
>>>>>> where the return value is:
>>>>>>
>>>>>>  -1 on module load error, and errp is set with the error
>>>>>>   0 on module or one of its dependencies are not installed
>>>>>>   1 on module load success
>>>>>>   2 on module load success (module already loaded or built-in)
>>>>>>
>>>>>> module_load_qom_one has been introduced in:
>>>>>>
>>>>>> commit 28457744c345 ("module: qom module support"), which built on top of
>>>>>> module_load_one, but discarded the bool return value. Restore it.
>>>>>>
>>>>>> Adapt all callers to emit errors, or ignore them, or fail hard,
>>>>>> as appropriate in each context.
>>>>>>
>>>>>> Some memory leaks also fixed as part of the module_load changes.
>>>>>>
>>>>>> audio: when attempting to load an audio module, report module load errors.
>>>>>> block: when attempting to load a block module, report module load errors.
>>>>>> console: when attempting to load a display module, report module load errors.
>>>>>>
>>>>>> qdev: when creating a new qdev Device object (DeviceState), report load errors.
>>>>>>       If a module cannot be loaded to create that device, now abort execution.
>>>>>>
>>>>>> qom/object.c: when initializing a QOM object, or looking up class_by_name,
>>>>>>               report module load errors.
>>>>>>
>>>>>> qtest: when processing the "module_load" qtest command, report errors
>>>>>>        in the load of the module.
>>>>>>
>>>>>> Signed-off-by: Claudio Fontana <cfontana@suse.de>
>>>>>
>>>>>> diff --git a/block/dmg.c b/block/dmg.c
>>>>>> index 007b8d9996..a422cf8d5b 100644
>>>>>> --- a/block/dmg.c
>>>>>> +++ b/block/dmg.c
>>>>>> @@ -434,6 +434,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>>>>>>      uint64_t plist_xml_offset, plist_xml_length;
>>>>>>      int64_t offset;
>>>>>>      int ret;
>>>>>> +    Error *local_err = NULL;
>>>>>>  
>>>>>>      ret = bdrv_apply_auto_read_only(bs, NULL, errp);
>>>>>>      if (ret < 0) {
>>>>>> @@ -446,8 +447,15 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
>>>>>>          return -EINVAL;
>>>>>>      }
>>>>>>  
>>>>>> -    block_module_load("dmg-bz2");
>>>>>> -    block_module_load("dmg-lzfse");
>>>>>> +    if (block_module_load("dmg-bz2", &local_err) < 0) {
>>>>>> +        error_report_err(local_err);
>>>>>> +        return -EINVAL;
>>>>>> +    }
>>>>>> +    local_err = NULL;
>>>>>> +    if (block_module_load("dmg-lzfse", &local_err) < 0) {
>>>>>> +        error_report_err(local_err);
>>>>>> +        return -EINVAL;
>>>>
>>>> I am concerned about the resources allocation here though,
>>>> is returning EINVAL here right, vs using "goto fail"?
>>>>
>>>> I matched the behavior of the preceding call:
>>>>
>>>>     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
>>>>                                BDRV_CHILD_IMAGE, false, errp);
>>>>     if (!bs->file) {
>>>>         return -EINVAL;
>>>>     }
>>>>
>>>> But afterwards the code goes:
>>>> .
>>>>     /* locate the UDIF trailer */
>>>>     offset = dmg_find_koly_offset(bs->file, errp);
>>>>     if (offset < 0) {
>>>>         ret = offset;
>>>>         goto fail;
>>>>     }
>>>>
>>>> Should the resources be freed or not in your view?
>>>
>>> Functions should generally fail cleanly, and that means undoing side
>>> effects such as allocations.
>>
>> This is pretty obvious.
>>
>> Note that this function does not perform the allocations,
>> bdrv_open is called with existing allocated resources,
>> and then frees those resources only in specific code paths.
> 
> These aren't allocated, the caller just allocates the BDRVDMGState and
> initialises it with zeros. In other words, at this point, the free
> functions don't do anything yet and you may or may not call them, it
> doesn't matter.
> 
> We have an explicit line like this after the module loading:
> 
>     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
> 
> It's completely unnecessary because the BDRVDMGState is already
> initialised like this, but if you think of this as the point where these
> fields come to life, using a direct return before it and 'goto fail'
> afterwards makes a little bit of sense at least.
> 
> As far as I can tell, only dmg_read_resource_fork() or
> dmg_read_plist_xml() actually allocate some memeory that needs to be
> undone, so some of the existing 'goto fail' instances could be direct
> returns. But they're not wrong either, so no need to change them.
> 
> Kevin
> 

Thanks Kevin,

given this for my hunk I'll keep returning immediately with EINVAL then.

Claudio


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

end of thread, other threads:[~2022-09-27 15:31 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-23 23:20 [PATCH v6 0/5] improve error handling for module load Claudio Fontana
2022-09-23 23:21 ` [PATCH v6 1/5] module: removed unused function argument "mayfail" Claudio Fontana
2022-09-23 23:21 ` [PATCH v6 2/5] module: rename module_load_one to module_load Claudio Fontana
2022-09-23 23:21 ` [PATCH v6 3/5] module: add Error arguments to module_load and module_load_qom Claudio Fontana
2022-09-26 10:38   ` Kevin Wolf
2022-09-26 13:28     ` Claudio Fontana
2022-09-26 13:54     ` Claudio Fontana
2022-09-27  7:54       ` Markus Armbruster
2022-09-27  9:13         ` Claudio Fontana
2022-09-27 11:53           ` Kevin Wolf
2022-09-27 12:54             ` Claudio Fontana
2022-09-27 11:57         ` Kevin Wolf
2022-09-23 23:21 ` [PATCH v6 4/5] dmg: warn when opening dmg images containing blocks of unknown type Claudio Fontana
2022-09-23 23:21 ` [PATCH v6 5/5] accel: abort if we fail to load the accelerator plugin Claudio Fontana
2022-09-24 12:35   ` Philippe Mathieu-Daudé via
2022-09-26  7:58     ` Claudio Fontana
2022-09-26 10:56       ` Kevin Wolf
2022-09-26 11:21         ` Claudio Fontana

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