ed1787d
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
ed1787d
From: Zhang Boyang <zhangboyang.id@gmail.com>
ed1787d
Date: Fri, 5 Aug 2022 00:51:20 +0800
ed1787d
Subject: [PATCH] font: Fix size overflow in grub_font_get_glyph_internal()
ed1787d
ed1787d
The length of memory allocation and file read may overflow. This patch
ed1787d
fixes the problem by using safemath macros.
ed1787d
ed1787d
There is a lot of code repetition like "(x * y + 7) / 8". It is unsafe
ed1787d
if overflow happens. This patch introduces grub_video_bitmap_calc_1bpp_bufsz().
ed1787d
It is safe replacement for such code. It has safemath-like prototype.
ed1787d
ed1787d
This patch also introduces grub_cast(value, pointer), it casts value to
ed1787d
typeof(*pointer) then store the value to *pointer. It returns true when
ed1787d
overflow occurs or false if there is no overflow. The semantics of arguments
ed1787d
and return value are designed to be consistent with other safemath macros.
ed1787d
ed1787d
Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
ed1787d
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
ed1787d
(cherry picked from commit 941d10ad6f1dcbd12fb613002249e29ba035f985)
ed1787d
---
ed1787d
 grub-core/font/font.c   | 17 +++++++++++++----
ed1787d
 include/grub/bitmap.h   | 18 ++++++++++++++++++
ed1787d
 include/grub/safemath.h |  2 ++
ed1787d
 3 files changed, 33 insertions(+), 4 deletions(-)
ed1787d
ed1787d
diff --git a/grub-core/font/font.c b/grub-core/font/font.c
ed1787d
index 2f09a4a55b..6a3fbebbd8 100644
ed1787d
--- a/grub-core/font/font.c
ed1787d
+++ b/grub-core/font/font.c
ed1787d
@@ -739,7 +739,8 @@ grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code)
ed1787d
       grub_int16_t xoff;
ed1787d
       grub_int16_t yoff;
ed1787d
       grub_int16_t dwidth;
ed1787d
-      int len;
ed1787d
+      grub_ssize_t len;
ed1787d
+      grub_size_t sz;
ed1787d
 
ed1787d
       if (index_entry->glyph)
ed1787d
 	/* Return cached glyph.  */
ed1787d
@@ -768,9 +769,17 @@ grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code)
ed1787d
 	  return 0;
ed1787d
 	}
ed1787d
 
ed1787d
-      len = (width * height + 7) / 8;
ed1787d
-      glyph = grub_malloc (sizeof (struct grub_font_glyph) + len);
ed1787d
-      if (!glyph)
ed1787d
+      /* Calculate real struct size of current glyph. */
ed1787d
+      if (grub_video_bitmap_calc_1bpp_bufsz (width, height, &len) ||
ed1787d
+	  grub_add (sizeof (struct grub_font_glyph), len, &sz))
ed1787d
+	{
ed1787d
+	  remove_font (font);
ed1787d
+	  return 0;
ed1787d
+	}
ed1787d
+
ed1787d
+      /* Allocate and initialize the glyph struct. */
ed1787d
+      glyph = grub_malloc (sz);
ed1787d
+      if (glyph == NULL)
ed1787d
 	{
ed1787d
 	  remove_font (font);
ed1787d
 	  return 0;
ed1787d
diff --git a/include/grub/bitmap.h b/include/grub/bitmap.h
ed1787d
index 5728f8ca3a..0d9603f619 100644
ed1787d
--- a/include/grub/bitmap.h
ed1787d
+++ b/include/grub/bitmap.h
ed1787d
@@ -23,6 +23,7 @@
ed1787d
 #include <grub/symbol.h>
ed1787d
 #include <grub/types.h>
ed1787d
 #include <grub/video.h>
ed1787d
+#include <grub/safemath.h>
ed1787d
 
ed1787d
 struct grub_video_bitmap
ed1787d
 {
ed1787d
@@ -79,6 +80,23 @@ grub_video_bitmap_get_height (struct grub_video_bitmap *bitmap)
ed1787d
   return bitmap->mode_info.height;
ed1787d
 }
ed1787d
 
ed1787d
+/*
ed1787d
+ * Calculate and store the size of data buffer of 1bit bitmap in result.
ed1787d
+ * Equivalent to "*result = (width * height + 7) / 8" if no overflow occurs.
ed1787d
+ * Return true when overflow occurs or false if there is no overflow.
ed1787d
+ * This function is intentionally implemented as a macro instead of
ed1787d
+ * an inline function. Although a bit awkward, it preserves data types for
ed1787d
+ * safemath macros and reduces macro side effects as much as possible.
ed1787d
+ *
ed1787d
+ * XXX: Will report false overflow if width * height > UINT64_MAX.
ed1787d
+ */
ed1787d
+#define grub_video_bitmap_calc_1bpp_bufsz(width, height, result) \
ed1787d
+({ \
ed1787d
+  grub_uint64_t _bitmap_pixels; \
ed1787d
+  grub_mul ((width), (height), &_bitmap_pixels) ? 1 : \
ed1787d
+    grub_cast (_bitmap_pixels / GRUB_CHAR_BIT + !!(_bitmap_pixels % GRUB_CHAR_BIT), (result)); \
ed1787d
+})
ed1787d
+
ed1787d
 void EXPORT_FUNC (grub_video_bitmap_get_mode_info) (struct grub_video_bitmap *bitmap,
ed1787d
 						    struct grub_video_mode_info *mode_info);
ed1787d
 
ed1787d
diff --git a/include/grub/safemath.h b/include/grub/safemath.h
ed1787d
index c17b89bba1..bb0f826de1 100644
ed1787d
--- a/include/grub/safemath.h
ed1787d
+++ b/include/grub/safemath.h
ed1787d
@@ -30,6 +30,8 @@
ed1787d
 #define grub_sub(a, b, res)	__builtin_sub_overflow(a, b, res)
ed1787d
 #define grub_mul(a, b, res)	__builtin_mul_overflow(a, b, res)
ed1787d
 
ed1787d
+#define grub_cast(a, res)	grub_add ((a), 0, (res))
ed1787d
+
ed1787d
 #else
ed1787d
 #error gcc 5.1 or newer or clang 3.8 or newer is required
ed1787d
 #endif