From 750f4bacd3262376ced3f837d8dc78f834ca233a Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Tue, 20 Jan 2015 19:33:20 +0100
Subject: [PATCH 157/506] * grub-core/disk/diskfilter.c: Validate volumes to
avoid division by zero.
---
ChangeLog | 5 ++
grub-core/disk/diskfilter.c | 123 ++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 123 insertions(+), 5 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 141aa06..fbdd560 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
2015-01-20 Vladimir Serbinenko <phcoder@gmail.com>
+ * grub-core/disk/diskfilter.c: Validate volumes to avoid division
+ by zero.
+
+2015-01-20 Vladimir Serbinenko <phcoder@gmail.com>
+
* include/grub/term.h: Avoid returining 0-sized terminal
as it may lead to division by zero.
diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c
index e8a3bcb..97ca546 100644
--- a/grub-core/disk/diskfilter.c
+++ b/grub-core/disk/diskfilter.c
@@ -483,6 +483,96 @@ grub_diskfilter_read_node (const struct grub_diskfilter_node *node,
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown node '%s'", node->name);
}
+
+static grub_err_t
+validate_segment (struct grub_diskfilter_segment *seg);
+
+static grub_err_t
+validate_lv (struct grub_diskfilter_lv *lv)
+{
+ unsigned int i;
+ if (!lv)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown volume");
+
+ if (lv->vg->extent_size == 0)
+ return grub_error (GRUB_ERR_READ_ERROR, "invalid volume");
+
+ for (i = 0; i < lv->segment_count; i++)
+ {
+ grub_err_t err;
+ err = validate_segment (&lv->segments[1]);
+ if (err)
+ return err;
+ }
+ return GRUB_ERR_NONE;
+}
+
+
+static grub_err_t
+validate_node (const struct grub_diskfilter_node *node)
+{
+ /* Check whether we actually know the physical volume we want to
+ read from. */
+ if (node->pv)
+ return GRUB_ERR_NONE;
+ if (node->lv)
+ return validate_lv (node->lv);
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown node '%s'", node->name);
+}
+
+static grub_err_t
+validate_segment (struct grub_diskfilter_segment *seg)
+{
+ grub_err_t err;
+
+ if (seg->stripe_size == 0 || seg->node_count == 0)
+ return grub_error(GRUB_ERR_BAD_FS, "invalid segment");
+
+ switch (seg->type)
+ {
+ case GRUB_DISKFILTER_RAID10:
+ {
+ grub_uint8_t near, far;
+ near = seg->layout & 0xFF;
+ far = (seg->layout >> 8) & 0xFF;
+ if ((seg->layout >> 16) == 0 && far == 0)
+ return grub_error(GRUB_ERR_BAD_FS, "invalid segment");
+ if (near > seg->node_count)
+ return grub_error(GRUB_ERR_BAD_FS, "invalid segment");
+ break;
+ }
+
+ case GRUB_DISKFILTER_STRIPED:
+ case GRUB_DISKFILTER_MIRROR:
+ break;
+
+ case GRUB_DISKFILTER_RAID4:
+ case GRUB_DISKFILTER_RAID5:
+ if (seg->node_count <= 1)
+ return grub_error(GRUB_ERR_BAD_FS, "invalid segment");
+ break;
+
+ case GRUB_DISKFILTER_RAID6:
+ if (seg->node_count <= 2)
+ return grub_error(GRUB_ERR_BAD_FS, "invalid segment");
+ break;
+
+ default:
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "unsupported RAID level %d", seg->type);
+ }
+
+ unsigned i;
+ for (i = 0; i < seg->node_count; i++)
+ {
+ err = validate_node (&seg->nodes[i]);
+ if (err)
+ return err;
+ }
+ return GRUB_ERR_NONE;
+
+}
+
static grub_err_t
read_segment (struct grub_diskfilter_segment *seg, grub_disk_addr_t sector,
grub_size_t size, char *buf)
@@ -848,6 +938,32 @@ grub_diskfilter_vg_register (struct grub_diskfilter_vg *vg)
for (lv = vg->lvs; lv; lv = lv->next)
{
+ /* RAID 1 and single-disk RAID 0 don't use a chunksize but code assumes one so set
+ one. */
+ for (i = 0; i < lv->segment_count; i++)
+ {
+ if (lv->segments[i].type == 1)
+ lv->segments[i].stripe_size = 64;
+ if (lv->segments[i].type == GRUB_DISKFILTER_STRIPED
+ && lv->segments[i].node_count == 1
+ && lv->segments[i].stripe_size == 0)
+ lv->segments[i].stripe_size = 64;
+ }
+ }
+
+ for (lv = vg->lvs; lv; lv = lv->next)
+ {
+ grub_err_t err;
+
+ /* RAID 1 doesn't use a chunksize but code assumes one so set
+ one. */
+ for (i = 0; i < lv->segment_count; i++)
+ if (lv->segments[i].type == 1)
+ lv->segments[i].stripe_size = 64;
+
+ err = validate_lv(lv);
+ if (err)
+ return err;
lv->number = lv_num++;
if (lv->fullname)
@@ -888,11 +1004,6 @@ grub_diskfilter_vg_register (struct grub_diskfilter_vg *vg)
lv->fullname = tmp;
}
}
- /* RAID 1 doesn't use a chunksize but code assumes one so set
- one. */
- for (i = 0; i < lv->segment_count; i++)
- if (lv->segments[i].type == 1)
- lv->segments[i].stripe_size = 64;
lv->vg = vg;
}
/* Add our new array to the list. */
@@ -926,6 +1037,8 @@ grub_diskfilter_make_raid (grub_size_t uuidlen, char *uuid, int nmemb,
n = layout & 0xFF;
if (n == 1)
n = (layout >> 8) & 0xFF;
+ if (n == 0)
+ return NULL;
totsize = grub_divmod64 (nmemb * disk_size, n, 0);
}
--
2.4.3