bootstd: Probe bootmeth devices for bootmeths env var
authorSam Protsenko <semen.protsenko@linaro.org>
Sun, 12 Jan 2025 03:42:12 +0000 (21:42 -0600)
committerTom Rini <trini@konsulko.com>
Fri, 20 Jun 2025 14:01:09 +0000 (08:01 -0600)
Specifying efi_mgr in 'bootmeths' environment variable leads to NULL
pointer dereference when 'bootflow scan' is executed, with call trace
like this:

    priv->fake_dev // NULL pointer dereference
    .read_bootflow = efi_mgr_read_bootflow()
    bootmeth_get_bootflow()
    bootflow_check()
    bootflow_scan_first()
    do_bootflow_scan()
    'bootflow scan -l'

That happens because in case when 'bootmeths' env var is defined the
bootmeth_efi_mgr driver is not probed, and the memory for its private
data isn't allocated by .priv_auto. In case when 'bootmeths' env var is
not defined, the std->bootmeth_count is 0, and the execution flow in
bootmeth_setup_iter_order() takes "no ordering" path, which in turn runs
uclass_get_device_by_seq() -> ... -> device_probe(), so issue isn't
present there. But when 'bootmeths' is defined and contains efi_mgr, the
std->bootmeth_count > 0, so bootmeth_setup_iter_order() follows the "we
have an ordering" path, where devices are not probed. In other words:

    'bootmeths' defined           'bootmeths' not defined
    --------------------------------------------------------
    priv == NULL                    priv != NULL
         ^                                ^
         |                        device_alloc_priv()
     no probe                     device_of_to_plat()
         ^                        device_probe()
         |                        uclass_get_device_tail()
    dev = order[i]                uclass_get_device_by_seq()
         ^                                ^
         | have an ordering               | no ordering
         +----------------+---------------+
                          |
             bootmeth_setup_iter_order()
             bootflow_scan_first()
             do_bootflow_scan()

Add an explicit device_probe() call in "we have an ordering" case to fix
the issue.

Fixes: c627cfc14c08 ("bootstd: Allow scanning for global bootmeths separately")
Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
boot/bootmeth-uclass.c

index cb84bb0..57b30bc 100644 (file)
@@ -12,6 +12,7 @@
 #include <bootmeth.h>
 #include <bootstd.h>
 #include <dm.h>
+#include <dm/device-internal.h>
 #include <env_internal.h>
 #include <fs.h>
 #include <malloc.h>
@@ -148,6 +149,12 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global)
                                struct bootmeth_uc_plat *ucp;
                                bool is_global;
 
+                               ret = device_probe(dev);
+                               if (ret) {
+                                       ret = log_msg_ret("probe", ret);
+                                       goto err_order;
+                               }
+
                                ucp = dev_get_uclass_plat(dev);
                                is_global = ucp->flags &
                                        BOOTMETHF_GLOBAL;