Correct several memory access violations and hangs found during fuzzing. Signed-off-by: Andrew Hamilton <adham...@gmail.com> --- 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 Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel