boot: make device_path_next_node() robust against malformed zero-length nodes

device_path_next_node() advances by the current node's Length field,
which per the EFI device path protocol includes the 4-byte node header;
a well-formed node is therefore at least sizeof(EFI_DEVICE_PATH) bytes
long. A malformed node with Length < sizeof(EFI_DEVICE_PATH), in
particular Length == 0, makes the helper return its input pointer
unchanged.

Advance by at least sizeof(EFI_DEVICE_PATH).

Follow-up for 5080a60a71
This commit is contained in:
Luca Boccassi
2026-07-03 15:19:38 +01:00
parent d06d8a232b
commit 3bcd707d56
2 changed files with 31 additions and 0 deletions
+24
View File
@@ -25,6 +25,10 @@ EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DE
const EFI_DEVICE_PATH *end_node = device_path_find_end_node(dp);
size_t file_size = strsize16(file);
/* The node Length is a uint16_t, so refuse a path that would not fit. */
if (file_size > UINT16_MAX - sizeof(FILEPATH_DEVICE_PATH))
return EFI_INVALID_PARAMETER;
size_t dp_size = (uint8_t *) end_node - (uint8_t *) dp;
/* Make a copy that can also hold a file media device path. */
@@ -55,6 +59,9 @@ EFI_STATUS make_url_device_path(const char16_t *url, EFI_DEVICE_PATH **ret) {
return EFI_INVALID_PARAMETER;
size_t l = strlen8(u);
/* The node Length is a uint16_t, so refuse a URL that would not fit. */
if (l > UINT16_MAX - offsetof(URI_DEVICE_PATH, Uri))
return EFI_INVALID_PARAMETER;
size_t t = offsetof(URI_DEVICE_PATH, Uri) + l + sizeof(EFI_DEVICE_PATH);
EFI_DEVICE_PATH *dp = xmalloc(t);
@@ -177,6 +184,23 @@ EFI_DEVICE_PATH *device_path_replace_node(
return ret;
}
bool device_path_is_valid(const EFI_DEVICE_PATH *dp, size_t size) {
if (!dp)
return false;
/* Validate against the known size so a truncated/corrupt path can't make us read out of bounds. */
for (;;) {
if (size < sizeof(EFI_DEVICE_PATH))
return false;
if (dp->Length < sizeof(EFI_DEVICE_PATH) || dp->Length > size)
return false;
if (device_path_is_end(dp))
return true;
size -= dp->Length;
dp = (const EFI_DEVICE_PATH *) ((const uint8_t *) dp + dp->Length);
}
}
size_t device_path_size(const EFI_DEVICE_PATH *dp) {
const EFI_DEVICE_PATH *i = ASSERT_PTR(dp);
+7
View File
@@ -13,6 +13,9 @@ EFI_DEVICE_PATH *device_path_replace_node(
static inline EFI_DEVICE_PATH *device_path_next_node(const EFI_DEVICE_PATH *dp) {
assert(dp);
/* The node Length includes the 4-byte header, so a well-formed node is at least that long. Paths
* coming from untrusted sources must be checked with device_path_is_valid() before being walked. */
assert(dp->Length >= sizeof(EFI_DEVICE_PATH));
return (EFI_DEVICE_PATH *) ((uint8_t *) dp + dp->Length);
}
@@ -30,4 +33,8 @@ static inline bool device_path_is_end(const EFI_DEVICE_PATH *dp) {
size_t device_path_size(const EFI_DEVICE_PATH *dp);
/* Validates that a device path is well-formed and fully contained within the given size, terminated by an
* end node. Use on paths from untrusted sources (e.g. EFI variables) before walking them. */
bool device_path_is_valid(const EFI_DEVICE_PATH *dp, size_t size);
EFI_DEVICE_PATH *device_path_dup(const EFI_DEVICE_PATH *dp);