From: Lidong Chen via Grub-devel <grub-devel@gnu.org>
To: grub-devel@gnu.org, daniel.kiper@oracle.com
Cc: Lidong Chen <lidong.chen@oracle.com>
Subject: [PATCH] disk/mdraid1x_linux: Prevent infinite recursion
Date: Mon, 29 Apr 2024 16:38:03 +0000 [thread overview]
Message-ID: <20240429163803.68572-1-lidong.chen@oracle.com> (raw)
The test corpus for version-1 RAID generated an infinite recursion
in grub_partition_iterate() while attempting to read the superblock.
The reason for the issue was that the data region overlapped with
the superblock.
The infinite call loop looks like this:
grub_partition_iterate() -> partmap->iterate() ->
-> grub_disk_read() -> grub_disk_read_small() ->
-> grub_disk_read_small_real() -> grub_diskfilter_read() ->
-> read_lv() -> read_segment() -> grub_diskfilter_read_node() ->
-> grub_disk_read() -> grub_disk_read_small() ->...
The fix adds checks for both the superblock region and the data
region when parsing the superblock metadata in grub_mdraid_detect().
Signed-off-by: Lidong Chen <lidong.chen@oracle.com>
---
grub-core/disk/mdraid1x_linux.c | 78 +++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/grub-core/disk/mdraid1x_linux.c b/grub-core/disk/mdraid1x_linux.c
index 72e5cb6f4..dd5d440a3 100644
--- a/grub-core/disk/mdraid1x_linux.c
+++ b/grub-core/disk/mdraid1x_linux.c
@@ -23,6 +23,7 @@
#include <grub/err.h>
#include <grub/misc.h>
#include <grub/diskfilter.h>
+#include <grub/safemath.h>
GRUB_MOD_LICENSE ("GPLv3+");
@@ -103,6 +104,9 @@ struct grub_raid_super_1x
#define WriteMostly1 1 /* Mask for writemostly flag in above devflags. */
+#define GRUB_MD_SECTOR_SHIFT 9 /* Follow Linux kernel v6.8. */
+#define GRUB_MD_SECTOR_SIZE (1 << GRUB_MD_SECTOR_SHIFT)
+
static struct grub_diskfilter_vg *
grub_mdraid_detect (grub_disk_t disk,
struct grub_diskfilter_pv_id *id,
@@ -129,6 +133,7 @@ grub_mdraid_detect (grub_disk_t disk,
grub_uint32_t level;
struct grub_diskfilter_vg *array;
char *uuid;
+ grub_uint64_t sb_sz, data_end, sb_end;
if (size == GRUB_DISK_SIZE_UNKNOWN && minor_version == 0)
continue;
@@ -154,6 +159,79 @@ grub_mdraid_detect (grub_disk_t disk,
|| grub_le_to_cpu64 (sb.super_offset) != sector)
continue;
+ /*
+ * The first check follows the Linux kernel's data_size
+ * validation from v6.8-rc5.
+ */
+ if (grub_le_to_cpu64 (sb.data_size) < 10 ||
+ grub_le_to_cpu64 (sb.raid_disks) > GRUB_MDRAID_MAX_DISKS)
+ {
+ grub_dprintf ("mdraid1x", "Corrupted superblock\n");
+ return NULL;
+ }
+
+ /*
+ * Total size of superblock: 256 bytes plus 2 bytes per device
+ * in the array.
+ */
+ sb_sz = sizeof (struct grub_raid_super_1x) + grub_le_to_cpu64 (sb.raid_disks) * 2;
+
+ if (grub_add (grub_le_to_cpu64 (sb.super_offset),
+ (ALIGN_UP(sb_sz, GRUB_MD_SECTOR_SIZE) >> GRUB_MD_SECTOR_SHIFT), &sb_end))
+ {
+ grub_dprintf ("mdraid1x", "Invalid superblock end.\n");
+ return NULL;
+ }
+
+ if (grub_add (grub_le_to_cpu64 (sb.data_offset),
+ grub_le_to_cpu64 (sb.data_size), &data_end))
+ {
+ grub_dprintf ("mdraid1x", "Invalid data end.\n");
+ return NULL;
+ }
+
+ /* In minor versions 1 and 2, superblock is positioned before data. */
+ if (minor_version)
+ {
+ if (grub_le_to_cpu64 (sb.data_offset) < sb_end)
+ {
+ grub_dprintf ("mdraid1x",
+ "The superblock either overlaps with the data "
+ "or is behind it.\n");
+ return NULL;
+ }
+
+ if (data_end > size)
+ {
+ grub_dprintf ("mdraid1x",
+ "The data region ends at %" PRIuGRUB_UINT64_T ", "
+ "past the end of the disk (%" PRIuGRUB_UINT64_T ")\n",
+ data_end, size);
+ return NULL;
+ }
+ }
+ else
+ {
+ /* In minor version 0, superblock is at the end of the device. */
+ if (grub_le_to_cpu64 (sb.super_offset) < data_end)
+ {
+ grub_dprintf ("mdraid1x",
+ "The data either overlaps with the superblock "
+ "or is behind it.\n");
+ return NULL;
+ }
+
+ if (sb_end > size)
+ {
+ grub_dprintf ("mdraid1x",
+ "The superblock region ends at "
+ "%" PRIuGRUB_UINT64_T ", past the end of "
+ "the disk (%" PRIuGRUB_UINT64_T ")\n",
+ sb_end, size);
+ return NULL;
+ }
+ }
+
if (sb.major_version != grub_cpu_to_le32_compile_time (1))
/* Unsupported version. */
return NULL;
--
2.34.1
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
next reply other threads:[~2024-04-29 16:39 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-04-29 16:38 Lidong Chen via Grub-devel [this message]
2024-05-06 18:26 ` [PATCH] disk/mdraid1x_linux: Prevent infinite recursion Daniel Kiper via Grub-devel
-- strict thread matches above, loose matches on Subject: below --
2024-02-29 22:13 Lidong Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240429163803.68572-1-lidong.chen@oracle.com \
--to=grub-devel@gnu.org \
--cc=daniel.kiper@oracle.com \
--cc=lidong.chen@oracle.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).