efi_loader: fix efi_dp_find_obj()
authorHeinrich Schuchardt <heinrich.schuchardt@canonical.com>
Fri, 4 Mar 2022 07:20:00 +0000 (08:20 +0100)
committerHeinrich Schuchardt <heinrich.schuchardt@canonical.com>
Sun, 20 Mar 2022 10:03:06 +0000 (11:03 +0100)
efi_dp_find_obj() should not return any handle with a partially matching
device path but the handle with the maximum matching device path.

Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
include/efi_loader.h
lib/efi_loader/efi_device_path.c

index 1ffcdfc..6271d40 100644 (file)
@@ -730,8 +730,8 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp);
 struct efi_device_path *efi_dp_next(const struct efi_device_path *dp);
 int efi_dp_match(const struct efi_device_path *a,
                 const struct efi_device_path *b);
-struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
-                                  struct efi_device_path **rem);
+efi_handle_t efi_dp_find_obj(struct efi_device_path *dp,
+                            struct efi_device_path **rem);
 /* get size of the first device path instance excluding end node */
 efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp);
 /* size of multi-instance device path excluding end node */
index ddd5f13..aeb5264 100644 (file)
@@ -159,69 +159,81 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp)
        return dp;
 }
 
-static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
-                                  struct efi_device_path **rem)
+/**
+ * find_handle() - find handle by device path
+ *
+ * If @rem is provided, the handle with the longest partial match is returned.
+ *
+ * @dp:                device path to search
+ * @short_path:        use short form device path for matching
+ * @rem:       pointer to receive remaining device path
+ * Return:     matching handle
+ */
+static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path,
+                               struct efi_device_path **rem)
 {
-       struct efi_object *efiobj;
-       efi_uintn_t dp_size = efi_dp_instance_size(dp);
+       efi_handle_t handle, best_handle = NULL;
+       efi_uintn_t len, best_len = 0;
+
+       len = efi_dp_instance_size(dp);
 
-       list_for_each_entry(efiobj, &efi_obj_list, link) {
+       list_for_each_entry(handle, &efi_obj_list, link) {
                struct efi_handler *handler;
-               struct efi_device_path *obj_dp;
+               struct efi_device_path *dp_current;
+               efi_uintn_t len_current;
                efi_status_t ret;
 
-               ret = efi_search_protocol(efiobj,
-                                         &efi_guid_device_path, &handler);
+               ret = efi_search_protocol(handle, &efi_guid_device_path,
+                                         &handler);
                if (ret != EFI_SUCCESS)
                        continue;
-               obj_dp = handler->protocol_interface;
-
-               do {
-                       if (efi_dp_match(dp, obj_dp) == 0) {
-                               if (rem) {
-                                       /*
-                                        * Allow partial matches, but inform
-                                        * the caller.
-                                        */
-                                       *rem = ((void *)dp) +
-                                               efi_dp_instance_size(obj_dp);
-                                       return efiobj;
-                               } else {
-                                       /* Only return on exact matches */
-                                       if (efi_dp_instance_size(obj_dp) ==
-                                           dp_size)
-                                               return efiobj;
-                               }
-                       }
-
-                       obj_dp = efi_dp_shorten(efi_dp_next(obj_dp));
-               } while (short_path && obj_dp);
+               dp_current = handler->protocol_interface;
+               if (short_path) {
+                       dp_current = efi_dp_shorten(dp_current);
+                       if (!dp_current)
+                               continue;
+               }
+               len_current = efi_dp_instance_size(dp_current);
+               if (rem) {
+                       if (len_current < len)
+                               continue;
+               } else {
+                       if (len_current != len)
+                               continue;
+               }
+               if (memcmp(dp_current, dp, len))
+                       continue;
+               if (!rem)
+                       return handle;
+               if (len_current > best_len) {
+                       best_len = len_current;
+                       best_handle = handle;
+                       *rem = (void*)((u8 *)dp + len_current);
+               }
        }
-
-       return NULL;
+       return best_handle;
 }
 
-/*
- * Find an efiobj from device-path, if 'rem' is not NULL, returns the
- * remaining part of the device path after the matched object.
+/**
+ * efi_dp_find_obj() - find handle by device path
+ *
+ * If @rem is provided, the handle with the longest partial match is returned.
+ *
+ * @dp:                device path to search
+ * @rem:       pointer to receive remaining device path
+ * Return:     matching handle
  */
-struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
-                                  struct efi_device_path **rem)
+efi_handle_t efi_dp_find_obj(struct efi_device_path *dp,
+                            struct efi_device_path **rem)
 {
-       struct efi_object *efiobj;
-
-       /* Search for an exact match first */
-       efiobj = find_obj(dp, false, NULL);
-
-       /* Then for a fuzzy match */
-       if (!efiobj)
-               efiobj = find_obj(dp, false, rem);
+       efi_handle_t handle;
 
-       /* And now for a fuzzy short match */
-       if (!efiobj)
-               efiobj = find_obj(dp, true, rem);
+       handle = find_handle(dp, false, rem);
+       if (!handle)
+               /* Match short form device path */
+               handle = find_handle(dp, true, rem);
 
-       return efiobj;
+       return handle;
 }
 
 /*