|
|
b1bcb2 |
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
c4e390 |
From: Peter Jones <pjones@redhat.com>
|
|
|
c4e390 |
Date: Sun, 19 Jul 2020 16:53:27 -0400
|
|
|
b1bcb2 |
Subject: [PATCH] efi: fix some malformed device path arithmetic errors.
|
|
|
c4e390 |
|
|
|
c4e390 |
Several places we take the length of a device path and subtract 4 from
|
|
|
c4e390 |
it, without ever checking that it's >= 4. There are also cases where
|
|
|
c4e390 |
this kind of malformation will result in unpredictable iteration,
|
|
|
c4e390 |
including treating the length from one dp node as the type in the next
|
|
|
c4e390 |
node. These are all errors, no matter where the data comes from.
|
|
|
c4e390 |
|
|
|
c4e390 |
This patch adds a checking macro, GRUB_EFI_DEVICE_PATH_VALID(), which
|
|
|
c4e390 |
can be used in several places, and makes GRUB_EFI_NEXT_DEVICE_PATH()
|
|
|
c4e390 |
return NULL and GRUB_EFI_END_ENTIRE_DEVICE_PATH() evaluate as true when
|
|
|
c4e390 |
the length is too small. Additionally, it makes several places in the
|
|
|
c4e390 |
code check for and return errors in these cases.
|
|
|
c4e390 |
|
|
|
c4e390 |
Signed-off-by: Peter Jones <pjones@redhat.com>
|
|
|
c4e390 |
Upstream-commit-id: 23e68a83990
|
|
|
c4e390 |
---
|
|
|
b1bcb2 |
grub-core/kern/efi/efi.c | 67 ++++++++++++++++++++++++++++++++------
|
|
|
b1bcb2 |
grub-core/loader/efi/chainloader.c | 19 +++++++++--
|
|
|
b1bcb2 |
grub-core/loader/i386/xnu.c | 9 ++---
|
|
|
b1bcb2 |
include/grub/efi/api.h | 14 +++++---
|
|
|
c4e390 |
4 files changed, 88 insertions(+), 21 deletions(-)
|
|
|
c4e390 |
|
|
|
c4e390 |
diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
|
|
|
c4e390 |
index 4bd7ca210ad..42c68307f3f 100644
|
|
|
c4e390 |
--- a/grub-core/kern/efi/efi.c
|
|
|
c4e390 |
+++ b/grub-core/kern/efi/efi.c
|
|
|
c4e390 |
@@ -325,7 +325,7 @@ grub_efi_get_filename (grub_efi_device_path_t *dp0)
|
|
|
c4e390 |
|
|
|
c4e390 |
dp = dp0;
|
|
|
c4e390 |
|
|
|
c4e390 |
- while (1)
|
|
|
c4e390 |
+ while (dp)
|
|
|
c4e390 |
{
|
|
|
c4e390 |
grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
|
|
|
c4e390 |
grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
|
|
|
c4e390 |
@@ -335,9 +335,15 @@ grub_efi_get_filename (grub_efi_device_path_t *dp0)
|
|
|
c4e390 |
if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
|
|
|
c4e390 |
&& subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE)
|
|
|
c4e390 |
{
|
|
|
c4e390 |
- grub_efi_uint16_t len;
|
|
|
c4e390 |
- len = ((GRUB_EFI_DEVICE_PATH_LENGTH (dp) - 4)
|
|
|
c4e390 |
- / sizeof (grub_efi_char16_t));
|
|
|
c4e390 |
+ grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
|
|
|
c4e390 |
+
|
|
|
c4e390 |
+ if (len < 4)
|
|
|
c4e390 |
+ {
|
|
|
c4e390 |
+ grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
|
c4e390 |
+ "malformed EFI Device Path node has length=%d", len);
|
|
|
c4e390 |
+ return NULL;
|
|
|
c4e390 |
+ }
|
|
|
c4e390 |
+ len = (len - 4) / sizeof (grub_efi_char16_t);
|
|
|
c4e390 |
filesize += GRUB_MAX_UTF8_PER_UTF16 * len + 2;
|
|
|
c4e390 |
}
|
|
|
c4e390 |
|
|
|
c4e390 |
@@ -353,7 +359,7 @@ grub_efi_get_filename (grub_efi_device_path_t *dp0)
|
|
|
c4e390 |
if (!name)
|
|
|
c4e390 |
return NULL;
|
|
|
c4e390 |
|
|
|
c4e390 |
- while (1)
|
|
|
c4e390 |
+ while (dp)
|
|
|
c4e390 |
{
|
|
|
c4e390 |
grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
|
|
|
c4e390 |
grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
|
|
|
c4e390 |
@@ -369,8 +375,15 @@ grub_efi_get_filename (grub_efi_device_path_t *dp0)
|
|
|
c4e390 |
|
|
|
c4e390 |
*p++ = '/';
|
|
|
c4e390 |
|
|
|
c4e390 |
- len = ((GRUB_EFI_DEVICE_PATH_LENGTH (dp) - 4)
|
|
|
c4e390 |
- / sizeof (grub_efi_char16_t));
|
|
|
c4e390 |
+ len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
|
|
|
c4e390 |
+ if (len < 4)
|
|
|
c4e390 |
+ {
|
|
|
c4e390 |
+ grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
|
c4e390 |
+ "malformed EFI Device Path node has length=%d", len);
|
|
|
c4e390 |
+ return NULL;
|
|
|
c4e390 |
+ }
|
|
|
c4e390 |
+
|
|
|
c4e390 |
+ len = (len - 4) / sizeof (grub_efi_char16_t);
|
|
|
c4e390 |
fp = (grub_efi_file_path_device_path_t *) dp;
|
|
|
c4e390 |
|
|
|
c4e390 |
dup_name = grub_calloc (len, sizeof (*dup_name));
|
|
|
c4e390 |
@@ -442,7 +455,26 @@ grub_efi_duplicate_device_path (const grub_efi_device_path_t *dp)
|
|
|
c4e390 |
;
|
|
|
c4e390 |
p = GRUB_EFI_NEXT_DEVICE_PATH (p))
|
|
|
c4e390 |
{
|
|
|
c4e390 |
- total_size += GRUB_EFI_DEVICE_PATH_LENGTH (p);
|
|
|
c4e390 |
+ grub_size_t len = GRUB_EFI_DEVICE_PATH_LENGTH (p);
|
|
|
c4e390 |
+
|
|
|
c4e390 |
+ /*
|
|
|
c4e390 |
+ * In the event that we find a node that's completely garbage, for
|
|
|
c4e390 |
+ * example if we get to 0x7f 0x01 0x02 0x00 ... (EndInstance with a size
|
|
|
c4e390 |
+ * of 2), GRUB_EFI_END_ENTIRE_DEVICE_PATH() will be true and
|
|
|
c4e390 |
+ * GRUB_EFI_NEXT_DEVICE_PATH() will return NULL, so we won't continue,
|
|
|
c4e390 |
+ * and neither should our consumers, but there won't be any error raised
|
|
|
c4e390 |
+ * even though the device path is junk.
|
|
|
c4e390 |
+ *
|
|
|
c4e390 |
+ * This keeps us from passing junk down back to our caller.
|
|
|
c4e390 |
+ */
|
|
|
c4e390 |
+ if (len < 4)
|
|
|
c4e390 |
+ {
|
|
|
c4e390 |
+ grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
|
c4e390 |
+ "malformed EFI Device Path node has length=%d", len);
|
|
|
c4e390 |
+ return NULL;
|
|
|
c4e390 |
+ }
|
|
|
c4e390 |
+
|
|
|
c4e390 |
+ total_size += len;
|
|
|
c4e390 |
if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (p))
|
|
|
c4e390 |
break;
|
|
|
c4e390 |
}
|
|
|
c4e390 |
@@ -487,7 +519,7 @@ dump_vendor_path (const char *type, grub_efi_vendor_device_path_t *vendor)
|
|
|
c4e390 |
void
|
|
|
c4e390 |
grub_efi_print_device_path (grub_efi_device_path_t *dp)
|
|
|
c4e390 |
{
|
|
|
c4e390 |
- while (1)
|
|
|
c4e390 |
+ while (GRUB_EFI_DEVICE_PATH_VALID (dp))
|
|
|
c4e390 |
{
|
|
|
c4e390 |
grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
|
|
|
c4e390 |
grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
|
|
|
c4e390 |
@@ -959,7 +991,11 @@ grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1,
|
|
|
c4e390 |
/* Return non-zero. */
|
|
|
c4e390 |
return 1;
|
|
|
c4e390 |
|
|
|
c4e390 |
- while (1)
|
|
|
c4e390 |
+ if (dp1 == dp2)
|
|
|
c4e390 |
+ return 0;
|
|
|
c4e390 |
+
|
|
|
c4e390 |
+ while (GRUB_EFI_DEVICE_PATH_VALID (dp1)
|
|
|
c4e390 |
+ && GRUB_EFI_DEVICE_PATH_VALID (dp2))
|
|
|
c4e390 |
{
|
|
|
c4e390 |
grub_efi_uint8_t type1, type2;
|
|
|
c4e390 |
grub_efi_uint8_t subtype1, subtype2;
|
|
|
c4e390 |
@@ -995,5 +1031,16 @@ grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1,
|
|
|
c4e390 |
dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2);
|
|
|
c4e390 |
}
|
|
|
c4e390 |
|
|
|
c4e390 |
+ /*
|
|
|
c4e390 |
+ * There's no "right" answer here, but we probably don't want to call a valid
|
|
|
c4e390 |
+ * dp and an invalid dp equal, so pick one way or the other.
|
|
|
c4e390 |
+ */
|
|
|
c4e390 |
+ if (GRUB_EFI_DEVICE_PATH_VALID (dp1) &&
|
|
|
c4e390 |
+ !GRUB_EFI_DEVICE_PATH_VALID (dp2))
|
|
|
c4e390 |
+ return 1;
|
|
|
c4e390 |
+ else if (!GRUB_EFI_DEVICE_PATH_VALID (dp1) &&
|
|
|
c4e390 |
+ GRUB_EFI_DEVICE_PATH_VALID (dp2))
|
|
|
c4e390 |
+ return -1;
|
|
|
c4e390 |
+
|
|
|
c4e390 |
return 0;
|
|
|
c4e390 |
}
|
|
|
c4e390 |
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
|
|
|
c4e390 |
index 3d600fa6bd5..b90dfd6c5a6 100644
|
|
|
c4e390 |
--- a/grub-core/loader/efi/chainloader.c
|
|
|
c4e390 |
+++ b/grub-core/loader/efi/chainloader.c
|
|
|
c4e390 |
@@ -125,6 +125,12 @@ copy_file_path (grub_efi_file_path_device_path_t *fp,
|
|
|
c4e390 |
fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE;
|
|
|
c4e390 |
fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE;
|
|
|
c4e390 |
|
|
|
c4e390 |
+ if (!GRUB_EFI_DEVICE_PATH_VALID ((grub_efi_device_path_t *)fp))
|
|
|
c4e390 |
+ {
|
|
|
c4e390 |
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "EFI Device Path is invalid");
|
|
|
c4e390 |
+ return;
|
|
|
c4e390 |
+ }
|
|
|
c4e390 |
+
|
|
|
c4e390 |
path_name = grub_calloc (len, GRUB_MAX_UTF16_PER_UTF8 * sizeof (*path_name));
|
|
|
c4e390 |
if (!path_name)
|
|
|
c4e390 |
return;
|
|
|
c4e390 |
@@ -164,9 +170,18 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename)
|
|
|
c4e390 |
|
|
|
c4e390 |
size = 0;
|
|
|
c4e390 |
d = dp;
|
|
|
c4e390 |
- while (1)
|
|
|
c4e390 |
+ while (d)
|
|
|
c4e390 |
{
|
|
|
c4e390 |
- size += GRUB_EFI_DEVICE_PATH_LENGTH (d);
|
|
|
c4e390 |
+ grub_size_t len = GRUB_EFI_DEVICE_PATH_LENGTH (d);
|
|
|
c4e390 |
+
|
|
|
c4e390 |
+ if (len < 4)
|
|
|
c4e390 |
+ {
|
|
|
c4e390 |
+ grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
|
c4e390 |
+ "malformed EFI Device Path node has length=%d", len);
|
|
|
c4e390 |
+ return NULL;
|
|
|
c4e390 |
+ }
|
|
|
c4e390 |
+
|
|
|
c4e390 |
+ size += len;
|
|
|
c4e390 |
if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d)))
|
|
|
c4e390 |
break;
|
|
|
c4e390 |
d = GRUB_EFI_NEXT_DEVICE_PATH (d);
|
|
|
c4e390 |
diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c
|
|
|
c4e390 |
index 424488a9f7c..8a68e49ad76 100644
|
|
|
c4e390 |
--- a/grub-core/loader/i386/xnu.c
|
|
|
c4e390 |
+++ b/grub-core/loader/i386/xnu.c
|
|
|
c4e390 |
@@ -512,14 +512,15 @@ grub_cmd_devprop_load (grub_command_t cmd __attribute__ ((unused)),
|
|
|
c4e390 |
|
|
|
c4e390 |
devhead = buf;
|
|
|
c4e390 |
buf = devhead + 1;
|
|
|
c4e390 |
- dpstart = buf;
|
|
|
c4e390 |
+ dp = dpstart = buf;
|
|
|
c4e390 |
|
|
|
c4e390 |
- do
|
|
|
c4e390 |
+ while (GRUB_EFI_DEVICE_PATH_VALID (dp) && buf < bufend)
|
|
|
c4e390 |
{
|
|
|
c4e390 |
- dp = buf;
|
|
|
c4e390 |
buf = (char *) buf + GRUB_EFI_DEVICE_PATH_LENGTH (dp);
|
|
|
c4e390 |
+ if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
|
|
|
c4e390 |
+ break;
|
|
|
c4e390 |
+ dp = buf;
|
|
|
c4e390 |
}
|
|
|
c4e390 |
- while (!GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp) && buf < bufend);
|
|
|
c4e390 |
|
|
|
c4e390 |
dev = grub_xnu_devprop_add_device (dpstart, (char *) buf
|
|
|
c4e390 |
- (char *) dpstart);
|
|
|
c4e390 |
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
|
|
|
c4e390 |
index fbd1c4b529d..9860fd16189 100644
|
|
|
c4e390 |
--- a/include/grub/efi/api.h
|
|
|
c4e390 |
+++ b/include/grub/efi/api.h
|
|
|
c4e390 |
@@ -613,6 +613,7 @@ typedef struct grub_efi_device_path grub_efi_device_path_protocol_t;
|
|
|
c4e390 |
#define GRUB_EFI_DEVICE_PATH_TYPE(dp) ((dp)->type & 0x7f)
|
|
|
c4e390 |
#define GRUB_EFI_DEVICE_PATH_SUBTYPE(dp) ((dp)->subtype)
|
|
|
c4e390 |
#define GRUB_EFI_DEVICE_PATH_LENGTH(dp) ((dp)->length)
|
|
|
c4e390 |
+#define GRUB_EFI_DEVICE_PATH_VALID(dp) ((dp) != NULL && GRUB_EFI_DEVICE_PATH_LENGTH (dp) >= 4)
|
|
|
c4e390 |
|
|
|
c4e390 |
/* The End of Device Path nodes. */
|
|
|
c4e390 |
#define GRUB_EFI_END_DEVICE_PATH_TYPE (0xff & 0x7f)
|
|
|
c4e390 |
@@ -621,13 +622,16 @@ typedef struct grub_efi_device_path grub_efi_device_path_protocol_t;
|
|
|
c4e390 |
#define GRUB_EFI_END_THIS_DEVICE_PATH_SUBTYPE 0x01
|
|
|
c4e390 |
|
|
|
c4e390 |
#define GRUB_EFI_END_ENTIRE_DEVICE_PATH(dp) \
|
|
|
c4e390 |
- (GRUB_EFI_DEVICE_PATH_TYPE (dp) == GRUB_EFI_END_DEVICE_PATH_TYPE \
|
|
|
c4e390 |
- && (GRUB_EFI_DEVICE_PATH_SUBTYPE (dp) \
|
|
|
c4e390 |
- == GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE))
|
|
|
c4e390 |
+ (!GRUB_EFI_DEVICE_PATH_VALID (dp) || \
|
|
|
c4e390 |
+ (GRUB_EFI_DEVICE_PATH_TYPE (dp) == GRUB_EFI_END_DEVICE_PATH_TYPE \
|
|
|
c4e390 |
+ && (GRUB_EFI_DEVICE_PATH_SUBTYPE (dp) \
|
|
|
c4e390 |
+ == GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE)))
|
|
|
c4e390 |
|
|
|
c4e390 |
#define GRUB_EFI_NEXT_DEVICE_PATH(dp) \
|
|
|
c4e390 |
- ((grub_efi_device_path_t *) ((char *) (dp) \
|
|
|
c4e390 |
- + GRUB_EFI_DEVICE_PATH_LENGTH (dp)))
|
|
|
c4e390 |
+ (GRUB_EFI_DEVICE_PATH_VALID (dp) \
|
|
|
c4e390 |
+ ? ((grub_efi_device_path_t *) \
|
|
|
c4e390 |
+ ((char *) (dp) + GRUB_EFI_DEVICE_PATH_LENGTH (dp))) \
|
|
|
c4e390 |
+ : NULL)
|
|
|
c4e390 |
|
|
|
c4e390 |
/* Hardware Device Path. */
|
|
|
c4e390 |
#define GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE 1
|