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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user