efi_loader: fix efi_dp_find_obj()

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>
This commit is contained in:
Heinrich Schuchardt 2022-03-04 08:20:00 +01:00
parent 8399488672
commit c409593d08
2 changed files with 63 additions and 51 deletions

View 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); struct efi_device_path *efi_dp_next(const struct efi_device_path *dp);
int efi_dp_match(const struct efi_device_path *a, int efi_dp_match(const struct efi_device_path *a,
const struct efi_device_path *b); const struct efi_device_path *b);
struct efi_object *efi_dp_find_obj(struct efi_device_path *dp, efi_handle_t efi_dp_find_obj(struct efi_device_path *dp,
struct efi_device_path **rem); struct efi_device_path **rem);
/* get size of the first device path instance excluding end node */ /* get size of the first device path instance excluding end node */
efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp); efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp);
/* size of multi-instance device path excluding end node */ /* size of multi-instance device path excluding end node */

View file

@ -159,69 +159,81 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp)
return 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_handle_t handle, best_handle = NULL;
efi_uintn_t dp_size = efi_dp_instance_size(dp); efi_uintn_t len, best_len = 0;
list_for_each_entry(efiobj, &efi_obj_list, link) { len = efi_dp_instance_size(dp);
list_for_each_entry(handle, &efi_obj_list, link) {
struct efi_handler *handler; 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; efi_status_t ret;
ret = efi_search_protocol(efiobj, ret = efi_search_protocol(handle, &efi_guid_device_path,
&efi_guid_device_path, &handler); &handler);
if (ret != EFI_SUCCESS) if (ret != EFI_SUCCESS)
continue; continue;
obj_dp = handler->protocol_interface; dp_current = handler->protocol_interface;
if (short_path) {
do { dp_current = efi_dp_shorten(dp_current);
if (efi_dp_match(dp, obj_dp) == 0) { if (!dp_current)
if (rem) { continue;
/* }
* Allow partial matches, but inform len_current = efi_dp_instance_size(dp_current);
* the caller. if (rem) {
*/ if (len_current < len)
*rem = ((void *)dp) + continue;
efi_dp_instance_size(obj_dp); } else {
return efiobj; if (len_current != len)
} else { continue;
/* Only return on exact matches */ }
if (efi_dp_instance_size(obj_dp) == if (memcmp(dp_current, dp, len))
dp_size) continue;
return efiobj; if (!rem)
} return handle;
} if (len_current > best_len) {
best_len = len_current;
obj_dp = efi_dp_shorten(efi_dp_next(obj_dp)); best_handle = handle;
} while (short_path && obj_dp); *rem = (void*)((u8 *)dp + len_current);
}
} }
return best_handle;
return NULL;
} }
/* /**
* Find an efiobj from device-path, if 'rem' is not NULL, returns the * efi_dp_find_obj() - find handle by device path
* remaining part of the device path after the matched object. *
* 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, efi_handle_t efi_dp_find_obj(struct efi_device_path *dp,
struct efi_device_path **rem) struct efi_device_path **rem)
{ {
struct efi_object *efiobj; efi_handle_t handle;
/* Search for an exact match first */ handle = find_handle(dp, false, rem);
efiobj = find_obj(dp, false, NULL); if (!handle)
/* Match short form device path */
handle = find_handle(dp, true, rem);
/* Then for a fuzzy match */ return handle;
if (!efiobj)
efiobj = find_obj(dp, false, rem);
/* And now for a fuzzy short match */
if (!efiobj)
efiobj = find_obj(dp, true, rem);
return efiobj;
} }
/* /*