malloc: Use overflow checking primitives where we do complex allocations

This attempts to fix the places where we do the following where
arithmetic_expr may include unvalidated data:

  X = grub_malloc(arithmetic_expr);

It accomplishes this by doing the arithmetic ahead of time using grub_add(),
grub_sub(), grub_mul() and testing for overflow before proceeding.

Among other issues, this fixes:
  - allocation of integer overflow in grub_video_bitmap_create()
    reported by Chris Coulson,
  - allocation of integer overflow in grub_png_decode_image_header()
    reported by Chris Coulson,
  - allocation of integer overflow in grub_squash_read_symlink()
    reported by Chris Coulson,
  - allocation of integer overflow in grub_ext2_read_symlink()
    reported by Chris Coulson,
  - allocation of integer overflow in read_section_as_string()
    reported by Chris Coulson.

Fixes: CVE-2020-14309, CVE-2020-14310, CVE-2020-14311

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
This commit is contained in:
Peter Jones 2020-06-15 12:28:27 -04:00 committed by Daniel Kiper
parent f725fa7cb2
commit 3f05d693d1
23 changed files with 382 additions and 113 deletions

View file

@ -32,6 +32,7 @@
#include <grub/auth.h>
#include <grub/disk.h>
#include <grub/partition.h>
#include <grub/safemath.h>
GRUB_MOD_LICENSE ("GPLv3+");
@ -104,13 +105,22 @@ legacy_file (const char *filename)
if (newsuffix)
{
char *t;
grub_size_t sz;
if (grub_add (grub_strlen (suffix), grub_strlen (newsuffix), &sz) ||
grub_add (sz, 1, &sz))
{
grub_errno = GRUB_ERR_OUT_OF_RANGE;
goto fail_0;
}
t = suffix;
suffix = grub_realloc (suffix, grub_strlen (suffix)
+ grub_strlen (newsuffix) + 1);
suffix = grub_realloc (suffix, sz);
if (!suffix)
{
grub_free (t);
fail_0:
grub_free (entrysrc);
grub_free (parsed);
grub_free (newsuffix);
@ -154,13 +164,22 @@ legacy_file (const char *filename)
else
{
char *t;
grub_size_t sz;
if (grub_add (grub_strlen (entrysrc), grub_strlen (parsed), &sz) ||
grub_add (sz, 1, &sz))
{
grub_errno = GRUB_ERR_OUT_OF_RANGE;
goto fail_1;
}
t = entrysrc;
entrysrc = grub_realloc (entrysrc, grub_strlen (entrysrc)
+ grub_strlen (parsed) + 1);
entrysrc = grub_realloc (entrysrc, sz);
if (!entrysrc)
{
grub_free (t);
fail_1:
grub_free (parsed);
grub_free (suffix);
return grub_errno;

View file

@ -23,6 +23,7 @@
#include <grub/file.h>
#include <grub/device.h>
#include <grub/script_sh.h>
#include <grub/safemath.h>
#include <regex.h>
@ -48,6 +49,7 @@ merge (char **dest, char **ps)
int i;
int j;
char **p;
grub_size_t sz;
if (! dest)
return ps;
@ -60,7 +62,12 @@ merge (char **dest, char **ps)
for (j = 0; ps[j]; j++)
;
p = grub_realloc (dest, sizeof (char*) * (i + j + 1));
if (grub_add (i, j, &sz) ||
grub_add (sz, 1, &sz) ||
grub_mul (sz, sizeof (char *), &sz))
return dest;
p = grub_realloc (dest, sz);
if (! p)
{
grub_free (dest);
@ -115,8 +122,15 @@ make_regex (const char *start, const char *end, regex_t *regexp)
char ch;
int i = 0;
unsigned len = end - start;
char *buffer = grub_malloc (len * 2 + 2 + 1); /* worst case size. */
char *buffer;
grub_size_t sz;
/* Worst case size is (len * 2 + 2 + 1). */
if (grub_mul (len, 2, &sz) ||
grub_add (sz, 3, &sz))
return 1;
buffer = grub_malloc (sz);
if (! buffer)
return 1;
@ -226,6 +240,7 @@ match_devices_iter (const char *name, void *data)
struct match_devices_ctx *ctx = data;
char **t;
char *buffer;
grub_size_t sz;
/* skip partitions if asked to. */
if (ctx->noparts && grub_strchr (name, ','))
@ -239,11 +254,16 @@ match_devices_iter (const char *name, void *data)
if (regexec (ctx->regexp, buffer, 0, 0, 0))
{
grub_dprintf ("expand", "not matched\n");
fail:
grub_free (buffer);
return 0;
}
t = grub_realloc (ctx->devs, sizeof (char*) * (ctx->ndev + 2));
if (grub_add (ctx->ndev, 2, &sz) ||
grub_mul (sz, sizeof (char *), &sz))
goto fail;
t = grub_realloc (ctx->devs, sz);
if (! t)
{
grub_free (buffer);
@ -300,6 +320,7 @@ match_files_iter (const char *name,
struct match_files_ctx *ctx = data;
char **t;
char *buffer;
grub_size_t sz;
/* skip . and .. names */
if (grub_strcmp(".", name) == 0 || grub_strcmp("..", name) == 0)
@ -315,9 +336,14 @@ match_files_iter (const char *name,
if (! buffer)
return 1;
t = grub_realloc (ctx->files, sizeof (char*) * (ctx->nfile + 2));
if (! t)
if (grub_add (ctx->nfile, 2, &sz) ||
grub_mul (sz, sizeof (char *), &sz))
goto fail;
t = grub_realloc (ctx->files, sz);
if (!t)
{
fail:
grub_free (buffer);
return 1;
}