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

@ -28,6 +28,7 @@
#include <grub/charset.h>
#include <grub/datetime.h>
#include <grub/udf.h>
#include <grub/safemath.h>
GRUB_MOD_LICENSE ("GPLv3+");
@ -890,9 +891,19 @@ read_string (const grub_uint8_t *raw, grub_size_t sz, char *outbuf)
utf16[i] = (raw[2 * i + 1] << 8) | raw[2*i + 2];
}
if (!outbuf)
outbuf = grub_malloc (utf16len * GRUB_MAX_UTF8_PER_UTF16 + 1);
{
grub_size_t size;
if (grub_mul (utf16len, GRUB_MAX_UTF8_PER_UTF16, &size) ||
grub_add (size, 1, &size))
goto fail;
outbuf = grub_malloc (size);
}
if (outbuf)
*grub_utf16_to_utf8 ((grub_uint8_t *) outbuf, utf16, utf16len) = '\0';
fail:
grub_free (utf16);
return outbuf;
}
@ -1005,7 +1016,7 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
grub_size_t sz = U64 (node->block.fe.file_size);
grub_uint8_t *raw;
const grub_uint8_t *ptr;
char *out, *optr;
char *out = NULL, *optr;
if (sz < 4)
return NULL;
@ -1013,14 +1024,16 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
if (!raw)
return NULL;
if (grub_udf_read_file (node, NULL, NULL, 0, sz, (char *) raw) < 0)
{
grub_free (raw);
return NULL;
}
goto fail_1;
out = grub_malloc (sz * 2 + 1);
if (grub_mul (sz, 2, &sz) ||
grub_add (sz, 1, &sz))
goto fail_0;
out = grub_malloc (sz);
if (!out)
{
fail_0:
grub_free (raw);
return NULL;
}
@ -1031,17 +1044,17 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
{
grub_size_t s;
if ((grub_size_t) (ptr - raw + 4) > sz)
goto fail;
goto fail_1;
if (!(ptr[2] == 0 && ptr[3] == 0))
goto fail;
goto fail_1;
s = 4 + ptr[1];
if ((grub_size_t) (ptr - raw + s) > sz)
goto fail;
goto fail_1;
switch (*ptr)
{
case 1:
if (ptr[1])
goto fail;
goto fail_1;
/* Fallthrough. */
case 2:
/* in 4 bytes. out: 1 byte. */
@ -1066,11 +1079,11 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
if (optr != out)
*optr++ = '/';
if (!read_string (ptr + 4, s - 4, optr))
goto fail;
goto fail_1;
optr += grub_strlen (optr);
break;
default:
goto fail;
goto fail_1;
}
ptr += s;
}
@ -1078,7 +1091,7 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
grub_free (raw);
return out;
fail:
fail_1:
grub_free (raw);
grub_free (out);
grub_error (GRUB_ERR_BAD_FS, "invalid symlink");