From e2e625065dcbf1dab82b9d1b0cb3e39f7b4f616a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:17 +0200 Subject: [PATCH] Checked the layout of attribute list entries Make sure the attribute list has at least one element, and that each of them has the minimal size and does not overflow out of the end of list. --- libntfs-3g/attrib.c | 34 ++++++++++++++++++++++++++++------ libntfs-3g/volume.c | 8 +++++--- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 83a2d603..12989615 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3049,8 +3049,21 @@ static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, le32_to_cpu(AT_ATTRIBUTE_LIST)) goto find_attr_list_attr; } else { + /* Check for small entry */ + if (((p2n(al_end) - p2n(ctx->al_entry)) + < (long)offsetof(ATTR_LIST_ENTRY, name)) + || (le16_to_cpu(ctx->al_entry->length) & 7) + || (le16_to_cpu(ctx->al_entry->length) + < offsetof(ATTR_LIST_ENTRY, name))) + goto corrupt; + al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry + le16_to_cpu(ctx->al_entry->length)); + if ((al_entry->name_length + && ((u8*)al_entry + al_entry->name_offset + + al_entry->name_length * sizeof(ntfschar)) + > al_end)) + goto corrupt; /* * If this is an enumeration and the attribute list attribute * is the next one in the enumeration sequence, just return the @@ -3113,11 +3126,18 @@ find_attr_list_attr: /* Catch the end of the attribute list. */ if ((u8*)al_entry == al_end) goto not_found; - if (!al_entry->length) - break; - if ((u8*)al_entry + 6 > al_end || (u8*)al_entry + - le16_to_cpu(al_entry->length) > al_end) - break; + + if ((((u8*)al_entry + offsetof(ATTR_LIST_ENTRY, name)) > al_end) + || ((u8*)al_entry + le16_to_cpu(al_entry->length) > al_end) + || (le16_to_cpu(al_entry->length) & 7) + || (le16_to_cpu(al_entry->length) + < offsetof(ATTR_LIST_ENTRY, name_length)) + || (al_entry->name_length + && ((u8*)al_entry + al_entry->name_offset + + al_entry->name_length * sizeof(ntfschar)) + > al_end)) + break; /* corrupt */ + next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry + le16_to_cpu(al_entry->length)); if (type != AT_UNUSED) { @@ -3270,13 +3290,15 @@ do_next_attr: a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); goto do_next_attr_loop; } +corrupt : if (ni != base_ni) { ctx->ntfs_ino = base_ni; ctx->mrec = ctx->base_mrec; ctx->attr = ctx->base_attr; } errno = EIO; - ntfs_log_perror("Inode is corrupt (%lld)", (long long)base_ni->mft_no); + ntfs_log_error("Corrupt attribute list entry in MFT record %lld\n", + (long long)base_ni->mft_no); return -1; not_found: /* diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index e1c93236..e6ec498b 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -306,10 +306,12 @@ static int ntfs_mft_load(ntfs_volume *vol) ntfs_log_error("Failed to get value of $MFT/$ATTR_LIST.\n"); goto io_error_exit; } - if (l != vol->mft_ni->attr_list_size) { + if ((l != vol->mft_ni->attr_list_size) + || (l < (s64)offsetof(ATTR_LIST_ENTRY, name))) { ntfs_log_error("Partial read of $MFT/$ATTR_LIST (%lld != " - "%u).\n", (long long)l, - vol->mft_ni->attr_list_size); + "%u or < %d).\n", (long long)l, + vol->mft_ni->attr_list_size, + (int)offsetof(ATTR_LIST_ENTRY, name)); goto io_error_exit; }