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