Blame SOURCES/0303-efi-fix-some-malformed-device-path-arithmetic-errors.patch

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