Correct several memory access violations and hangs found during fuzzing.
Signed-off-by: Andrew Hamilton <[email protected]>
---
grub-core/fs/ntfs.c | 95 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 80 insertions(+), 15 deletions(-)
diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c
index 0d087acd8..45fff2400 100644
--- a/grub-core/fs/ntfs.c
+++ b/grub-core/fs/ntfs.c
@@ -246,6 +246,7 @@ fixup (grub_uint8_t *buf, grub_size_t len, const
grub_uint8_t *magic)
grub_uint16_t ss;
grub_uint8_t *pu;
grub_uint16_t us;
+ grub_uint16_t pu_offset;
COMPILE_TIME_ASSERT ((1 << GRUB_NTFS_BLK_SHR) == GRUB_DISK_SECTOR_SIZE);
@@ -255,7 +256,10 @@ fixup (grub_uint8_t *buf, grub_size_t len, const
grub_uint8_t *magic)
ss = u16at (buf, 6) - 1;
if (ss != len)
return grub_error (GRUB_ERR_BAD_FS, "size not match");
- pu = buf + u16at (buf, 4);
+ pu_offset = u16at (buf, 4);
+ if (pu_offset >= (len * GRUB_DISK_SECTOR_SIZE - (2 * ss)))
+ return grub_error (GRUB_ERR_BAD_FS, "pu offset size incorrect");
+ pu = buf + pu_offset;
us = u16at (pu, 0);
buf -= 2;
while (ss > 0)
@@ -373,6 +377,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr)
new_pos = &at->emft_buf[first_attr_off (at->emft_buf)];
end = &at->emft_buf[emft_buf_size];
+ at->end = end;
while (new_pos && *new_pos != 0xFF)
{
@@ -395,7 +400,8 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr)
}
at->attr_cur = at->attr_nxt;
mft_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR);
- while (at->attr_cur >= at->mft->buf && at->attr_cur < mft_end &&
*at->attr_cur != 0xFF)
+ while (at->attr_cur >= at->mft->buf && at->attr_cur < (mft_end - 4)
+ && *at->attr_cur != 0xFF)
{
/* We can't use validate_attribute here because this logic
* seems to be used for both parsing through attributes
@@ -412,7 +418,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr)
if (*at->attr_cur == GRUB_NTFS_AT_ATTRIBUTE_LIST)
at->attr_end = at->attr_cur;
- if ((*at->attr_cur == attr) || (attr == 0))
+ if ((*at->attr_cur == attr) || (attr == 0) || (nsize == 0))
return at->attr_cur;
at->attr_cur = at->attr_nxt;
}
@@ -442,13 +448,23 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr)
return NULL;
}
at->attr_nxt = at->edat_buf;
- at->attr_end = at->edat_buf + u32at (pa, 0x30);
+ grub_uint32_t edat_offset = u32at (pa, 0x30);
+ if (edat_offset >= n) {
+ grub_error (GRUB_ERR_BAD_FS, "edat offset is out of bounds");
+ return NULL;
+ }
+ at->attr_end = at->edat_buf + edat_offset;
pa_end = at->edat_buf + n;
}
else
{
at->attr_nxt = at->attr_end + res_attr_data_off (pa);
- at->attr_end = at->attr_end + u32at (pa, 4);
+ grub_uint32_t edat_offset = u32at (pa, 4);
+ if ((at->attr_end + edat_offset) >= (at->end)) {
+ grub_error (GRUB_ERR_BAD_FS, "edat offset is out of bounds");
+ return NULL;
+ }
+ at->attr_end = at->attr_end + edat_offset;
pa_end = at->mft->buf + (at->mft->data->mft_size <<
GRUB_NTFS_BLK_SHR);
}
at->flags |= GRUB_NTFS_AF_ALST;
@@ -470,7 +486,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr)
at->attr_nxt = NULL;
}
- if (at->attr_nxt >= at->attr_end || at->attr_nxt == NULL)
+ if ((at->attr_nxt + GRUB_NTFS_ATTRIBUTE_HEADER_SIZE) >= at->attr_end ||
at->attr_nxt == NULL)
return NULL;
if ((at->flags & GRUB_NTFS_AF_MMFT) && (attr == GRUB_NTFS_AT_DATA))
@@ -537,13 +553,16 @@ locate_attr (struct grub_ntfs_attr *at, struct
grub_ntfs_file *mft,
return NULL;
if ((at->flags & GRUB_NTFS_AF_ALST) == 0)
{
+ /* Used to make sure we're not stuck in a loop. */
+ grub_uint8_t *last_pa = NULL;
while (1)
{
pa = find_attr (at, attr);
- if (pa == NULL)
+ if (pa == NULL || pa == last_pa)
break;
if (at->flags & GRUB_NTFS_AF_ALST)
return pa;
+ last_pa = pa;
}
grub_errno = GRUB_ERR_NONE;
free_attr (at);
@@ -639,6 +658,7 @@ read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa,
grub_uint8_t *dest,
grub_disk_read_hook_t read_hook, void *read_hook_data)
{
struct grub_ntfs_rlst cc, *ctx;
+ grub_uint8_t *end_ptr = (pa + len);
if (len == 0)
return 0;
@@ -671,7 +691,13 @@ read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa,
grub_uint8_t *dest,
return 0;
}
- ctx->cur_run = pa + u16at (pa, 0x20);
+ grub_uint16_t run_offset = u16at (pa, 0x20);
+ if ((run_offset + pa) >= end_ptr || ((run_offset + pa) >= (at->end)))
+ {
+ return grub_error (GRUB_ERR_BAD_FS, "run offset out of range");
+ }
+
+ ctx->cur_run = pa + run_offset;
ctx->next_vcn = u32at (pa, 0x10);
ctx->curr_lcn = 0;
@@ -734,6 +760,8 @@ read_attr (struct grub_ntfs_attr *at, grub_uint8_t *dest,
grub_disk_addr_t ofs,
grub_uint8_t *pp;
grub_err_t ret;
+ if (at == NULL || at->attr_cur == NULL)
+ return grub_error (GRUB_ERR_BAD_FS, "attribute not found");
save_cur = at->attr_cur;
at->attr_nxt = at->attr_cur;
attr = *at->attr_nxt;
@@ -830,8 +858,11 @@ init_file (struct grub_ntfs_file *mft, grub_uint64_t mftno)
static void
free_file (struct grub_ntfs_file *mft)
{
- free_attr (&mft->attr);
- grub_free (mft->buf);
+ if (mft)
+ {
+ free_attr (&mft->attr);
+ grub_free (mft->buf);
+ }
}
static char *
@@ -924,7 +955,12 @@ list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos,
grub_uint8_t *end_pos
grub_free (ustr);
}
- pos += u16at (pos, 8);
+ grub_uint16_t pos_incr = u16at (pos, 8);
+ if (pos_incr > 0)
+ pos += pos_incr;
+ else
+ return 0;
+
}
return 0;
}
@@ -1035,6 +1071,8 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir,
int ret = 0;
grub_size_t bitmap_len;
struct grub_ntfs_file *mft;
+ /* Used to make sure we're not stuck in a loop. */
+ grub_uint8_t *last_pos = NULL;
mft = (struct grub_ntfs_file *) dir;
@@ -1054,18 +1092,22 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir,
while (1)
{
cur_pos = find_attr (at, GRUB_NTFS_AT_INDEX_ROOT);
- if (cur_pos == NULL)
+ if (cur_pos == NULL || cur_pos == last_pos)
{
grub_error (GRUB_ERR_BAD_FS, "no $INDEX_ROOT");
goto done;
}
+ last_pos = cur_pos;
+
/* Resident, Namelen=4, Offset=0x18, Flags=0x00, Name="$I30" */
if ((u32at (cur_pos, 8) != 0x180400) ||
(u32at (cur_pos, 0x18) != 0x490024) ||
(u32at (cur_pos, 0x1C) != 0x300033))
continue;
cur_pos += res_attr_data_off (cur_pos);
+ if(cur_pos >= at->end)
+ continue;
if (*cur_pos != 0x30) /* Not filename index */
continue;
break;
@@ -1084,10 +1126,18 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir,
/* No need to check errors here, as it will already be fine */
init_attr (at, mft);
+ last_pos = NULL;
while ((cur_pos = find_attr (at, GRUB_NTFS_AT_BITMAP)) != NULL)
{
int ofs;
+ if (cur_pos == last_pos)
+ {
+ grub_error (GRUB_ERR_BAD_FS, "bitmap attribute loop");
+ goto done;
+ }
+ last_pos = cur_pos;
+
ofs = cur_pos[0xA];
/* Namelen=4, Name="$I30" */
if ((cur_pos[9] == 4) &&
@@ -1135,7 +1185,17 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir,
"fails to read non-resident $BITMAP");
goto done;
}
- bitmap_len = u32at (cur_pos, 0x30);
+ grub_uint32_t tmp_len = u32at (cur_pos, 0x30);
+ if (tmp_len <= bitmap_len)
+ {
+ bitmap_len = tmp_len;
+ }
+ else
+ {
+ grub_error (GRUB_ERR_BAD_FS,
+ "bitmap len too large for non-resident $BITMAP");
+ goto done;
+ }
}
bitmap = bmp;
@@ -1144,6 +1204,7 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir,
}
free_attr (at);
+ last_pos = NULL;
cur_pos = locate_attr (at, mft, GRUB_NTFS_AT_INDEX_ALLOCATION);
while (cur_pos != NULL)
{
@@ -1153,6 +1214,9 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir,
(u32at (cur_pos, 0x44) == 0x300033))
break;
cur_pos = find_attr (at, GRUB_NTFS_AT_INDEX_ALLOCATION);
+ if (cur_pos == last_pos)
+ break;
+ last_pos = cur_pos;
}
if ((!cur_pos) && (bitmap))
@@ -1480,7 +1544,7 @@ grub_ntfs_label (grub_device_t device, char **label)
pa = find_attr (&mft->attr, GRUB_NTFS_AT_VOLUME_NAME);
- if (pa >= mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR))
+ if (pa == NULL || pa >= mft->buf + (mft->data->mft_size <<
GRUB_NTFS_BLK_SHR))
{
grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label");
goto fail;
@@ -1498,7 +1562,8 @@ grub_ntfs_label (grub_device_t device, char **label)
len = res_attr_data_len (pa) / 2;
pa += res_attr_data_off (pa);
- if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa >= 2 *
len)
+ if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa >= 2 *
len &&
+ pa >= mft->buf && (pa + len < (mft->buf + (mft->data->mft_size <<
GRUB_NTFS_BLK_SHR))))
*label = get_utf8 (pa, len);
else
grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label");
--
2.39.5
_______________________________________________
Grub-devel mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/grub-devel