relocator: Protect grub_relocator_alloc_chunk_align() max_addr against integer underflow
This commit introduces integer underflow mitigation in max_addr calculation in grub_relocator_alloc_chunk_align() invocation. It consists of 2 fixes: 1. Introduced grub_relocator_alloc_chunk_align_safe() wrapper function to perform sanity check for min/max and size values, and to make safe invocation of grub_relocator_alloc_chunk_align() with validated max_addr value. Replace all invocations such as grub_relocator_alloc_chunk_align(..., min_addr, max_addr - size, size, ...) by grub_relocator_alloc_chunk_align_safe(..., min_addr, max_addr, size, ...). 2. Introduced UP_TO_TOP32(s) macro for the cases where max_addr is 32-bit top address (0xffffffff - size + 1) or similar. Signed-off-by: Alexey Makhalov <amakhalov@vmware.com> Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
This commit is contained in:
parent
caea56d1f8
commit
61ff5602fe
13 changed files with 69 additions and 58 deletions
|
@ -83,8 +83,7 @@ grub_relocator32_boot (struct grub_relocator *rel,
|
||||||
/* Specific memory range due to Global Descriptor Table for use by payload
|
/* Specific memory range due to Global Descriptor Table for use by payload
|
||||||
that we will store in returned chunk. The address range and preference
|
that we will store in returned chunk. The address range and preference
|
||||||
are based on "THE LINUX/x86 BOOT PROTOCOL" specification. */
|
are based on "THE LINUX/x86 BOOT PROTOCOL" specification. */
|
||||||
err = grub_relocator_alloc_chunk_align (rel, &ch, 0x1000,
|
err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x1000, 0x9a000,
|
||||||
0x9a000 - RELOCATOR_SIZEOF (32),
|
|
||||||
RELOCATOR_SIZEOF (32), 16,
|
RELOCATOR_SIZEOF (32), 16,
|
||||||
GRUB_RELOCATOR_PREFERENCE_LOW,
|
GRUB_RELOCATOR_PREFERENCE_LOW,
|
||||||
avoid_efi_bootservices);
|
avoid_efi_bootservices);
|
||||||
|
@ -125,13 +124,10 @@ grub_relocator16_boot (struct grub_relocator *rel,
|
||||||
grub_relocator_chunk_t ch;
|
grub_relocator_chunk_t ch;
|
||||||
|
|
||||||
/* Put it higher than the byte it checks for A20 check. */
|
/* Put it higher than the byte it checks for A20 check. */
|
||||||
err = grub_relocator_alloc_chunk_align (rel, &ch, 0x8010,
|
err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x8010, 0xa0000,
|
||||||
0xa0000 - RELOCATOR_SIZEOF (16)
|
RELOCATOR_SIZEOF (16) +
|
||||||
- GRUB_RELOCATOR16_STACK_SIZE,
|
GRUB_RELOCATOR16_STACK_SIZE, 16,
|
||||||
RELOCATOR_SIZEOF (16)
|
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
||||||
+ GRUB_RELOCATOR16_STACK_SIZE, 16,
|
|
||||||
GRUB_RELOCATOR_PREFERENCE_NONE,
|
|
||||||
0);
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -183,11 +179,9 @@ grub_relocator64_boot (struct grub_relocator *rel,
|
||||||
void *relst;
|
void *relst;
|
||||||
grub_relocator_chunk_t ch;
|
grub_relocator_chunk_t ch;
|
||||||
|
|
||||||
err = grub_relocator_alloc_chunk_align (rel, &ch, min_addr,
|
err = grub_relocator_alloc_chunk_align_safe (rel, &ch, min_addr, max_addr,
|
||||||
max_addr - RELOCATOR_SIZEOF (64),
|
|
||||||
RELOCATOR_SIZEOF (64), 16,
|
RELOCATOR_SIZEOF (64), 16,
|
||||||
GRUB_RELOCATOR_PREFERENCE_NONE,
|
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
||||||
0);
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|
|
@ -120,10 +120,8 @@ grub_relocator32_boot (struct grub_relocator *rel,
|
||||||
unsigned i;
|
unsigned i;
|
||||||
grub_addr_t vtarget;
|
grub_addr_t vtarget;
|
||||||
|
|
||||||
err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
|
err = grub_relocator_alloc_chunk_align (rel, &ch, 0, UP_TO_TOP32 (stateset_size),
|
||||||
(0xffffffff - stateset_size)
|
stateset_size, sizeof (grub_uint32_t),
|
||||||
+ 1, stateset_size,
|
|
||||||
sizeof (grub_uint32_t),
|
|
||||||
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -115,10 +115,8 @@ grub_relocator32_boot (struct grub_relocator *rel,
|
||||||
unsigned i;
|
unsigned i;
|
||||||
grub_relocator_chunk_t ch;
|
grub_relocator_chunk_t ch;
|
||||||
|
|
||||||
err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
|
err = grub_relocator_alloc_chunk_align (rel, &ch, 0, UP_TO_TOP32 (stateset_size),
|
||||||
(0xffffffff - stateset_size)
|
stateset_size, sizeof (grub_uint32_t),
|
||||||
+ 1, stateset_size,
|
|
||||||
sizeof (grub_uint32_t),
|
|
||||||
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -50,8 +50,7 @@ grub_relocator64_efi_boot (struct grub_relocator *rel,
|
||||||
* 64-bit relocator code may live above 4 GiB quite well.
|
* 64-bit relocator code may live above 4 GiB quite well.
|
||||||
* However, I do not want ask for problems. Just in case.
|
* However, I do not want ask for problems. Just in case.
|
||||||
*/
|
*/
|
||||||
err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
|
err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0, 0x100000000,
|
||||||
0x100000000 - RELOCATOR_SIZEOF (64_efi),
|
|
||||||
RELOCATOR_SIZEOF (64_efi), 16,
|
RELOCATOR_SIZEOF (64_efi), 16,
|
||||||
GRUB_RELOCATOR_PREFERENCE_NONE, 1);
|
GRUB_RELOCATOR_PREFERENCE_NONE, 1);
|
||||||
if (err)
|
if (err)
|
||||||
|
|
|
@ -181,9 +181,8 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align,
|
||||||
for (; err && *align + 1 > min_align; (*align)--)
|
for (; err && *align + 1 > min_align; (*align)--)
|
||||||
{
|
{
|
||||||
grub_errno = GRUB_ERR_NONE;
|
grub_errno = GRUB_ERR_NONE;
|
||||||
err = grub_relocator_alloc_chunk_align (relocator, &ch,
|
err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000,
|
||||||
0x1000000,
|
UP_TO_TOP32 (prot_size),
|
||||||
0xffffffff & ~prot_size,
|
|
||||||
prot_size, 1 << *align,
|
prot_size, 1 << *align,
|
||||||
GRUB_RELOCATOR_PREFERENCE_LOW,
|
GRUB_RELOCATOR_PREFERENCE_LOW,
|
||||||
1);
|
1);
|
||||||
|
|
|
@ -466,9 +466,8 @@ grub_multiboot_make_mbi (grub_uint32_t *target)
|
||||||
|
|
||||||
bufsize = grub_multiboot_get_mbi_size ();
|
bufsize = grub_multiboot_get_mbi_size ();
|
||||||
|
|
||||||
err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
|
err = grub_relocator_alloc_chunk_align_safe (grub_multiboot_relocator, &ch,
|
||||||
0x10000, 0xa0000 - bufsize,
|
0x10000, 0xa0000, bufsize, 4,
|
||||||
bufsize, 4,
|
|
||||||
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -453,10 +453,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
||||||
|
|
||||||
{
|
{
|
||||||
grub_relocator_chunk_t ch;
|
grub_relocator_chunk_t ch;
|
||||||
err = grub_relocator_alloc_chunk_align (relocator, &ch,
|
err = grub_relocator_alloc_chunk_align_safe (relocator, &ch, addr_min, addr_max, size,
|
||||||
addr_min, addr_max - size,
|
0x1000, GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
|
||||||
size, 0x1000,
|
|
||||||
GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
initrd_chunk = get_virtual_current_address (ch);
|
initrd_chunk = get_virtual_current_address (ch);
|
||||||
|
|
|
@ -442,12 +442,9 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
||||||
{
|
{
|
||||||
grub_relocator_chunk_t ch;
|
grub_relocator_chunk_t ch;
|
||||||
|
|
||||||
err = grub_relocator_alloc_chunk_align (relocator, &ch,
|
err = grub_relocator_alloc_chunk_align_safe (relocator, &ch, (target_addr & 0x1fffffff) +
|
||||||
(target_addr & 0x1fffffff)
|
linux_size + 0x10000, 0x10000000, size,
|
||||||
+ linux_size + 0x10000,
|
0x10000, GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
||||||
(0x10000000 - size),
|
|
||||||
size, 0x10000,
|
|
||||||
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
|
@ -403,7 +403,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
|
||||||
{
|
{
|
||||||
grub_relocator_chunk_t ch;
|
grub_relocator_chunk_t ch;
|
||||||
err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch,
|
err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch,
|
||||||
lowest_addr, (0xffffffff - size) + 1,
|
lowest_addr, UP_TO_TOP32 (size),
|
||||||
size, MULTIBOOT_MOD_ALIGN,
|
size, MULTIBOOT_MOD_ALIGN,
|
||||||
GRUB_RELOCATOR_PREFERENCE_NONE, 1);
|
GRUB_RELOCATOR_PREFERENCE_NONE, 1);
|
||||||
if (err)
|
if (err)
|
||||||
|
|
|
@ -109,8 +109,8 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
|
||||||
if (load_size > mld->max_addr || mld->min_addr > mld->max_addr - load_size)
|
if (load_size > mld->max_addr || mld->min_addr > mld->max_addr - load_size)
|
||||||
return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
|
return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
|
||||||
|
|
||||||
err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch,
|
err = grub_relocator_alloc_chunk_align_safe (GRUB_MULTIBOOT (relocator), &ch,
|
||||||
mld->min_addr, mld->max_addr - load_size,
|
mld->min_addr, mld->max_addr,
|
||||||
load_size, mld->align ? mld->align : 1,
|
load_size, mld->align ? mld->align : 1,
|
||||||
mld->preference, mld->avoid_efi_boot_services);
|
mld->preference, mld->avoid_efi_boot_services);
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch, 0,
|
err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch, 0,
|
||||||
(0xffffffff - sh->sh_size) + 1,
|
UP_TO_TOP32 (sh->sh_size),
|
||||||
sh->sh_size, sh->sh_addralign,
|
sh->sh_size, sh->sh_addralign,
|
||||||
GRUB_RELOCATOR_PREFERENCE_NONE,
|
GRUB_RELOCATOR_PREFERENCE_NONE,
|
||||||
mld->avoid_efi_boot_services);
|
mld->avoid_efi_boot_services);
|
||||||
|
|
|
@ -301,8 +301,8 @@ grub_multiboot2_load (grub_file_t file, const char *filename)
|
||||||
return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
|
return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
|
||||||
}
|
}
|
||||||
|
|
||||||
err = grub_relocator_alloc_chunk_align (grub_multiboot2_relocator, &ch,
|
err = grub_relocator_alloc_chunk_align_safe (grub_multiboot2_relocator, &ch,
|
||||||
mld.min_addr, mld.max_addr - code_size,
|
mld.min_addr, mld.max_addr,
|
||||||
code_size, mld.align ? mld.align : 1,
|
code_size, mld.align ? mld.align : 1,
|
||||||
mld.preference, keep_bs);
|
mld.preference, keep_bs);
|
||||||
}
|
}
|
||||||
|
@ -714,7 +714,7 @@ grub_multiboot2_make_mbi (grub_uint32_t *target)
|
||||||
COMPILE_TIME_ASSERT (MULTIBOOT_TAG_ALIGN % sizeof (grub_properly_aligned_t) == 0);
|
COMPILE_TIME_ASSERT (MULTIBOOT_TAG_ALIGN % sizeof (grub_properly_aligned_t) == 0);
|
||||||
|
|
||||||
err = grub_relocator_alloc_chunk_align (grub_multiboot2_relocator, &ch,
|
err = grub_relocator_alloc_chunk_align (grub_multiboot2_relocator, &ch,
|
||||||
MBI_MIN_ADDR, 0xffffffff - bufsize,
|
MBI_MIN_ADDR, UP_TO_TOP32 (bufsize),
|
||||||
bufsize, MULTIBOOT_TAG_ALIGN,
|
bufsize, MULTIBOOT_TAG_ALIGN,
|
||||||
GRUB_RELOCATOR_PREFERENCE_NONE, 1);
|
GRUB_RELOCATOR_PREFERENCE_NONE, 1);
|
||||||
if (err)
|
if (err)
|
||||||
|
|
|
@ -129,7 +129,7 @@ grub_xnu_resume (char *imagename)
|
||||||
{
|
{
|
||||||
grub_relocator_chunk_t ch;
|
grub_relocator_chunk_t ch;
|
||||||
err = grub_relocator_alloc_chunk_align (grub_xnu_relocator, &ch, 0,
|
err = grub_relocator_alloc_chunk_align (grub_xnu_relocator, &ch, 0,
|
||||||
(0xffffffff - hibhead.image_size) + 1,
|
UP_TO_TOP32 (hibhead.image_size),
|
||||||
hibhead.image_size,
|
hibhead.image_size,
|
||||||
GRUB_XNU_PAGESIZE,
|
GRUB_XNU_PAGESIZE,
|
||||||
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
|
||||||
|
|
|
@ -49,6 +49,35 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel,
|
||||||
int preference,
|
int preference,
|
||||||
int avoid_efi_boot_services);
|
int avoid_efi_boot_services);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wrapper for grub_relocator_alloc_chunk_align() with purpose of
|
||||||
|
* protecting against integer underflow.
|
||||||
|
*
|
||||||
|
* Compare to its callee, max_addr has different meaning here.
|
||||||
|
* It covers entire chunk and not just start address of the chunk.
|
||||||
|
*/
|
||||||
|
static inline grub_err_t
|
||||||
|
grub_relocator_alloc_chunk_align_safe (struct grub_relocator *rel,
|
||||||
|
grub_relocator_chunk_t *out,
|
||||||
|
grub_phys_addr_t min_addr,
|
||||||
|
grub_phys_addr_t max_addr,
|
||||||
|
grub_size_t size, grub_size_t align,
|
||||||
|
int preference,
|
||||||
|
int avoid_efi_boot_services)
|
||||||
|
{
|
||||||
|
/* Sanity check and ensure following equation (max_addr - size) is safe. */
|
||||||
|
if (max_addr < size || (max_addr - size) < min_addr)
|
||||||
|
return GRUB_ERR_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
return grub_relocator_alloc_chunk_align (rel, out, min_addr,
|
||||||
|
max_addr - size,
|
||||||
|
size, align, preference,
|
||||||
|
avoid_efi_boot_services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top 32-bit address minus s bytes and plus 1 byte. */
|
||||||
|
#define UP_TO_TOP32(s) ((~(s) & 0xffffffff) + 1)
|
||||||
|
|
||||||
#define GRUB_RELOCATOR_PREFERENCE_NONE 0
|
#define GRUB_RELOCATOR_PREFERENCE_NONE 0
|
||||||
#define GRUB_RELOCATOR_PREFERENCE_LOW 1
|
#define GRUB_RELOCATOR_PREFERENCE_LOW 1
|
||||||
#define GRUB_RELOCATOR_PREFERENCE_HIGH 2
|
#define GRUB_RELOCATOR_PREFERENCE_HIGH 2
|
||||||
|
|
Loading…
Reference in a new issue