commit a59b854381d1148d32f74fbb9cf0f384972a5b4b Author: Colin Watson Date: Sat Jul 25 12:15:37 2020 +0100 linux: Fix integer overflows in initrd size handling These could be triggered by a crafted filesystem with very large files. Fixes: CVE-2020-15707 Signed-off-by: Colin Watson Reviewed-by: Jan Setje-Eilers diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c index 25624ebc1..e9f819ee9 100644 --- a/grub-core/loader/linux.c +++ b/grub-core/loader/linux.c @@ -4,6 +4,7 @@ #include #include #include +#include struct newc_head { @@ -98,13 +99,13 @@ free_dir (struct dir *root) grub_free (root); } -static grub_size_t +static grub_err_t insert_dir (const char *name, struct dir **root, - grub_uint8_t *ptr) + grub_uint8_t *ptr, grub_size_t *size) { struct dir *cur, **head = root; const char *cb, *ce = name; - grub_size_t size = 0; + *size = 0; while (1) { for (cb = ce; *cb == '/'; cb++); @@ -130,14 +131,22 @@ insert_dir (const char *name, struct dir **root, ptr = make_header (ptr, name, ce - name, 040777, 0); } - size += ALIGN_UP ((ce - (char *) name) - + sizeof (struct newc_head), 4); + if (grub_add (*size, + ALIGN_UP ((ce - (char *) name) + + sizeof (struct newc_head), 4), + size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + grub_free (n->name); + grub_free (n); + return grub_errno; + } *head = n; cur = n; } root = &cur->next; } - return size; + return GRUB_ERR_NONE; } grub_err_t @@ -173,26 +182,33 @@ grub_initrd_init (int argc, char *argv[], eptr = grub_strchr (ptr, ':'); if (eptr) { + grub_size_t dir_size, name_len; + initrd_ctx->components[i].newc_name = grub_strndup (ptr, eptr - ptr); - if (!initrd_ctx->components[i].newc_name) + if (!initrd_ctx->components[i].newc_name || + insert_dir (initrd_ctx->components[i].newc_name, &root, 0, + &dir_size)) { grub_initrd_close (initrd_ctx); return grub_errno; } - initrd_ctx->size - += ALIGN_UP (sizeof (struct newc_head) - + grub_strlen (initrd_ctx->components[i].newc_name), - 4); - initrd_ctx->size += insert_dir (initrd_ctx->components[i].newc_name, - &root, 0); + name_len = grub_strlen (initrd_ctx->components[i].newc_name); + if (grub_add (initrd_ctx->size, + ALIGN_UP (sizeof (struct newc_head) + name_len, 4), + &initrd_ctx->size) || + grub_add (initrd_ctx->size, dir_size, &initrd_ctx->size)) + goto overflow; newc = 1; fname = eptr + 1; } } else if (newc) { - initrd_ctx->size += ALIGN_UP (sizeof (struct newc_head) - + sizeof ("TRAILER!!!") - 1, 4); + if (grub_add (initrd_ctx->size, + ALIGN_UP (sizeof (struct newc_head) + + sizeof ("TRAILER!!!") - 1, 4), + &initrd_ctx->size)) + goto overflow; free_dir (root); root = 0; newc = 0; @@ -208,19 +224,29 @@ grub_initrd_init (int argc, char *argv[], initrd_ctx->nfiles++; initrd_ctx->components[i].size = grub_file_size (initrd_ctx->components[i].file); - initrd_ctx->size += initrd_ctx->components[i].size; + if (grub_add (initrd_ctx->size, initrd_ctx->components[i].size, + &initrd_ctx->size)) + goto overflow; } if (newc) { initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4); - initrd_ctx->size += ALIGN_UP (sizeof (struct newc_head) - + sizeof ("TRAILER!!!") - 1, 4); + if (grub_add (initrd_ctx->size, + ALIGN_UP (sizeof (struct newc_head) + + sizeof ("TRAILER!!!") - 1, 4), + &initrd_ctx->size)) + goto overflow; free_dir (root); root = 0; } return GRUB_ERR_NONE; + +overflow: + free_dir (root); + grub_initrd_close (initrd_ctx); + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); } grub_size_t @@ -261,8 +287,16 @@ grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, if (initrd_ctx->components[i].newc_name) { - ptr += insert_dir (initrd_ctx->components[i].newc_name, - &root, ptr); + grub_size_t dir_size; + + if (insert_dir (initrd_ctx->components[i].newc_name, &root, ptr, + &dir_size)) + { + free_dir (root); + grub_initrd_close (initrd_ctx); + return grub_errno; + } + ptr += dir_size; ptr = make_header (ptr, initrd_ctx->components[i].newc_name, grub_strlen (initrd_ctx->components[i].newc_name), 0100777,