From e5ee8b325c8fd3b931c2b4e3d840c53626bbf876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 5 Mar 2021 10:05:24 +0100 Subject: [PATCH 01/52] Signalled support of UTIME_OMIT to external libfuse2 The external libfuse2 optionally uses UTIME_OMIT to tell whether mtime or ctime should not be changed in a utimensat(2) call. Set the flag flag_utime_omit_ok so that it knows that ntfs-3g supports this feature. Note : this is the default with internal libfuse and with libfuse3. --- src/ntfs-3g.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 6ec2020a..3494952c 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -3965,6 +3965,9 @@ static struct fuse_operations ntfs_3g_ops = { .rmdir = ntfs_fuse_rmdir, #ifdef HAVE_UTIMENSAT .utimens = ntfs_fuse_utimens, +#if defined(linux) & !defined(FUSE_INTERNAL) & (FUSE_VERSION < 30) + .flag_utime_omit_ok = 1, +#endif /* defined(linux) & !defined(FUSE_INTERNAL) */ #else .utime = ntfs_fuse_utime, #endif From 2ede3b67eef84260d2955fb6d4429ccb2db1ab1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 5 Mar 2021 10:21:36 +0100 Subject: [PATCH 02/52] Improved checking of the location of MFT Terminate cleanly when the MFT is improperly located, by making sure it is located within the volume and that it contains its own data. --- libntfs-3g/volume.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 7a1bcf29..416ebfb7 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -355,6 +355,11 @@ mft_has_no_attr_list: ntfs_log_perror("ntfs_mapping_pairs_decompress() failed"); goto error_exit; } + /* Make sure $DATA is the MFT itself */ + if (nrl->lcn != vol->mft_lcn) { + ntfs_log_perror("The MFT is not self-contained"); + goto error_exit; + } vol->mft_na->rl = nrl; /* Get the lowest vcn for the next extent. */ @@ -601,6 +606,10 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, vol->mft_zone_end = vol->mft_lcn + mft_zone_size; while (vol->mft_zone_end >= vol->nr_clusters) { mft_zone_size >>= 1; + if (!mft_zone_size) { + errno = EINVAL; + goto error_exit; + } vol->mft_zone_end = vol->mft_lcn + mft_zone_size; } ntfs_log_debug("mft_zone_end = 0x%llx\n", (long long)vol->mft_zone_end); From 477a4dfe5e2d7db3b56763d5aec64fac40324f17 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:31 +0200 Subject: [PATCH 03/52] ntfscp.c: Fix improper unsigned const endianness macro for signed type. --- ntfsprogs/ntfscp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ntfsprogs/ntfscp.c b/ntfsprogs/ntfscp.c index f30be5a7..19303b6f 100644 --- a/ntfsprogs/ntfscp.c +++ b/ntfsprogs/ntfscp.c @@ -1155,7 +1155,7 @@ close_attr: if (!fstat(fileno(in),&st)) { s64 change_time = st.st_mtime*10000000LL + NTFS_TIME_OFFSET; - out->last_data_change_time = cpu_to_le64(change_time); + out->last_data_change_time = cpu_to_sle64(change_time); ntfs_inode_update_times(out, 0); } else { ntfs_log_error("Failed to get the time stamp.\n"); From 3a535150164bb44f4d826935370a9722e407ca98 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:32 +0200 Subject: [PATCH 04/52] Update README to mention support for Windows 8 and 10 NTFS formats. --- README | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README b/README index b829fd0b..aa457ecc 100644 --- a/README +++ b/README @@ -5,7 +5,8 @@ INTRODUCTION The NTFS-3G driver is an open source, freely available read/write NTFS driver for Linux, FreeBSD, Mac OS X, NetBSD, OpenSolaris, QNX and Haiku. It provides safe and fast handling of the Windows XP, Windows Server 2003, Windows 2000, -Windows Vista, Windows Server 2008 and Windows 7 file systems. +Windows Vista, Windows Server 2008, Windows 7, Windows 8 and Windows 10 file +systems. The purpose of the project is to develop, quality assurance and support a trustable, featureful and high performance solution for hardware platforms From 83b66b3f39f55a97469e3c202bef3c308bdd9224 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:34 +0200 Subject: [PATCH 05/52] Change http links in README to https for security. --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index aa457ecc..8cc6e917 100644 --- a/README +++ b/README @@ -22,13 +22,13 @@ and creating internally compressed files (parameter files in the directory available in Windows 10 can also be read through a plugin. For using advanced features, please get the instructions from - http://www.tuxera.com/community/ntfs-3g-advanced/ + https://www.tuxera.com/community/ntfs-3g-advanced/ News, support answers, problem submission instructions, support and discussion forums, performance numbers and other information are available on the project web site at - http://www.tuxera.com/community/ + https://www.tuxera.com/community/ LICENSES ======== From e464ca75740116a6c77e769d4fb5da3fbb93fa84 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:36 +0200 Subject: [PATCH 06/52] Update last two entries in README to match style of previous entries. --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index 8cc6e917..5c000743 100644 --- a/README +++ b/README @@ -160,6 +160,6 @@ ntfscat - Concatenate files and print their contents on the standard output. ntfscp - Overwrite files on an NTFS partition. -ntfssecaudit : audit the security metadata. +ntfssecaudit - Audit the security metadata. -ntfsusermap : assistance for building a user mapping file. +ntfsusermap - Assistance for building a user mapping file. From 442e12fc32c3d3657dbc0233449c0d1121c79d3a Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:38 +0200 Subject: [PATCH 07/52] Update outdated copyright notice. --- src/lowntfs-3g.c | 2 +- src/ntfs-3g.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index badf0529..efc1a036 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -263,7 +263,7 @@ static const char *usage_msg = "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" "Copyright (C) 2007-2021 Jean-Pierre Andre\n" -"Copyright (C) 2009 Erik Larsson\n" +"Copyright (C) 2009-2020 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" "\n" diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 3494952c..fffe5f38 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -198,7 +198,7 @@ static const char *usage_msg = "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" "Copyright (C) 2007-2021 Jean-Pierre Andre\n" -"Copyright (C) 2009 Erik Larsson\n" +"Copyright (C) 2009-2020 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" "\n" From a4a837025b6ac2b0c44c93e34e22535fe9e95b27 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 5 Mar 2021 14:56:40 +0200 Subject: [PATCH 08/52] Add the official git repository location to the README file. --- README | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README b/README index 5c000743..f072c0ea 100644 --- a/README +++ b/README @@ -30,6 +30,10 @@ web site at https://www.tuxera.com/community/ +The project's official git repository is now hosted on GitHub: + + https://github.com/tuxera/ntfs-3g + LICENSES ======== From 1258474e07e1fe587ead4003926ae71e5e55dcf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:30:17 +0300 Subject: [PATCH 09/52] Improved check of upcase table length The consistency check on the upcase size was laxist. The unicode points are limited to 1048575 (20 bits), but there is no provision to support code points beyond 65535. In the current implementation the upcase size must be even and <= 131072. --- libntfs-3g/volume.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 416ebfb7..c6356563 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -1057,19 +1057,19 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); if (!na) { ntfs_log_perror("Failed to open ntfs attribute"); + ntfs_inode_close(ni); goto error_exit; } /* * Note: Normally, the upcase table has a length equal to 65536 - * 2-byte Unicode characters but allow for different cases, so no - * checks done. Just check we don't overflow 32-bits worth of Unicode - * characters. + * 2-byte Unicode characters. Anyway we currently can only process + * such characters. */ - if (na->data_size & ~0x1ffffffffULL) { - ntfs_log_error("Error: Upcase table is too big (max 32-bit " - "allowed).\n"); + if ((na->data_size - 2) & ~0x1fffeULL) { + ntfs_log_error("Error: Upcase table is invalid (want size even " + "<= 131072).\n"); errno = EINVAL; - goto error_exit; + goto bad_upcase; } if (vol->upcase_len != na->data_size >> 1) { vol->upcase_len = na->data_size >> 1; @@ -1077,7 +1077,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) free(vol->upcase); vol->upcase = ntfs_malloc(na->data_size); if (!vol->upcase) - goto error_exit; + goto bad_upcase; } /* Read in the $DATA attribute value into the buffer. */ l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase); @@ -1086,7 +1086,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) "(%lld != %lld).\n", (long long)l, (long long)na->data_size); errno = EIO; - goto error_exit; + goto bad_upcase; } /* Done with the $UpCase mft record. */ ntfs_attr_close(na); @@ -1291,6 +1291,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) } return vol; +bad_upcase : + ntfs_attr_close(na); + ntfs_inode_close(ni); + goto error_exit; io_error_exit: errno = EIO; error_exit: From 00a54786251611bc3241f2ec8d052f10ca514e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:31:52 +0300 Subject: [PATCH 10/52] Added check of layout of attributes Make sure the attributes fully lie within the MFT record, and make sure their variable components (name, runlist, value) fully lie within the attribute. --- libntfs-3g/attrib.c | 78 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 7 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index a736a9a2..89af840d 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -5,7 +5,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2007 Yura Pakhuchiy - * Copyright (c) 2007-2020 Jean-Pierre Andre + * Copyright (c) 2007-2021 Jean-Pierre Andre * Copyright (c) 2010 Erik Larsson * * This program/include file is free software; you can redistribute it and/or @@ -2765,6 +2765,8 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, ATTR_RECORD *a; ntfs_volume *vol; ntfschar *upcase; + ptrdiff_t offs; + ptrdiff_t space; u32 upcase_len; ntfs_log_trace("attribute type 0x%x.\n", le32_to_cpu(type)); @@ -2794,8 +2796,17 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, a = (ATTR_RECORD*)((char*)ctx->attr + le32_to_cpu(ctx->attr->length)); for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { - if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec + - le32_to_cpu(ctx->mrec->bytes_allocated)) + /* + * Make sure the attribute fully lies within the MFT record + * and we can safely access its minimal fields. + */ + offs = p2n(a) - p2n(ctx->mrec); + space = le32_to_cpu(ctx->mrec->bytes_allocated) - offs; + if ((offs < 0) + || (((space < (ptrdiff_t)offsetof(ATTR_RECORD, + resident_end)) + || (space < (ptrdiff_t)le32_to_cpu(a->length))) + && ((space < 4) || (a->type != AT_END)))) break; ctx->attr = a; if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > @@ -2986,6 +2997,8 @@ static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, u8 *al_start, *al_end; ATTR_RECORD *a; ntfschar *al_name; + ptrdiff_t offs; + ptrdiff_t space; u32 al_name_len; BOOL is_first_search = FALSE; @@ -3202,12 +3215,18 @@ is_enumeration: * with the same meanings as above. */ do_next_attr_loop: - if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec + - le32_to_cpu(ctx->mrec->bytes_allocated)) + /* + * Make sure the attribute fully lies within the MFT record + * and we can safely access its minimal fields. + */ + offs = p2n(a) - p2n(ctx->mrec); + space = le32_to_cpu(ctx->mrec->bytes_allocated) - offs; + if (offs < 0) break; - if (a->type == AT_END) + if ((space >= 4) && (a->type == AT_END)) continue; - if (!a->length) + if ((space < (ptrdiff_t)offsetof(ATTR_RECORD, resident_end)) + || (space < (ptrdiff_t)le32_to_cpu(a->length))) break; if (al_entry->instance != a->instance) goto do_next_attr; @@ -3373,6 +3392,7 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, { ntfs_volume *vol; ntfs_inode *base_ni; + ATTR_RECORD *a; int ret = -1; ntfs_log_enter("Entering for attribute type 0x%x\n", le32_to_cpu(type)); @@ -3394,6 +3414,50 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, else ret = ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn, val, val_len, ctx); + if (!ret) { + /* + * The attribute was found to fully lie within the MFT + * record, now make sure its relevant parts (name, runlist, + * value) also lie within. The first step is to make sure + * the attribute has the minimum length so that accesses to + * the lengths and offsets of these parts are safe. + */ + a = ctx->attr; + if (a->non_resident) { + if ((le32_to_cpu(a->length) + < offsetof(ATTR_RECORD, non_resident_end)) + || (le16_to_cpu(a->mapping_pairs_offset) + >= le32_to_cpu(a->length)) + || (a->name_length + && ((le16_to_cpu(a->name_offset) + + a->name_length) + > le32_to_cpu(a->length)))) { + ntfs_log_error("Corrupt non resident attribute" + " 0x%x in MFT record %lld\n", + (int)le32_to_cpu(a->type), + (long long)ctx->ntfs_ino->mft_no); + errno = EIO; + ret = -1; + } + } else { + if ((le32_to_cpu(a->length) + < offsetof(ATTR_RECORD, resident_end)) + || (le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length)) + > le32_to_cpu(a->length) + || (a->name_length + && ((le16_to_cpu(a->name_offset) + + a->name_length) + > le32_to_cpu(a->length)))) { + ntfs_log_error("Corrupt resident attribute 0x%x in" + " MFT record %lld\n", + (int)le32_to_cpu(a->type), + (long long)ctx->ntfs_ino->mft_no); + errno = EIO; + ret = -1; + } + } + } out: ntfs_log_leave("\n"); return ret; From 3f3b771cb08c92f34e21326462be3330973f02a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:33:04 +0300 Subject: [PATCH 11/52] Added consistency checks of index root The index root must lie within the space allocated for it. --- libntfs-3g/dir.c | 10 ++++++++++ libntfs-3g/index.c | 18 +++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index e85c3c52..f3c14d8f 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -293,6 +293,16 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, (unsigned)index_block_size); goto put_err_out; } + if (((offsetof(INDEX_ROOT,index) + + le32_to_cpu(ir->index.allocated_size)) + > le32_to_cpu(ctx->attr->value_length)) + || (le32_to_cpu(ir->index.entries_offset) + > le32_to_cpu(ir->index.index_length)) + || (le32_to_cpu(ir->index.index_length) + > le32_to_cpu(ir->index.allocated_size))) { + ntfs_log_error("Index root is corrupt.\n"); + goto put_err_out; + } index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); /* The first index entry. */ ie = (INDEX_ENTRY*)((u8*)&ir->index + diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 006a6ecd..e12cbafd 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005-2006 Yura Pakhuchiy * Copyright (c) 2005-2008 Szabolcs Szakacsits - * Copyright (c) 2007-2020 Jean-Pierre Andre + * Copyright (c) 2007-2021 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -673,6 +673,7 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_BLOCK *ib = NULL; + ATTR_RECORD *a; int ret, err = 0; ntfs_log_trace("Entering\n"); @@ -712,10 +713,17 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic } old_vcn = VCN_INDEX_ROOT_PARENT; - /* - * FIXME: check for both ir and ib that the first index entry is - * within the index block. - */ + a = icx->actx->attr; + if (((offsetof(INDEX_ROOT,index) + + le32_to_cpu(ir->index.index_length)) + > le32_to_cpu(a->value_length)) + || (le32_to_cpu(ir->index.entries_offset) + > le32_to_cpu(ir->index.index_length))) { + ntfs_log_error("Index root is corrupt in MFT record %lld.\n", + (long long)icx->ni->mft_no); + err = errno = ERANGE; + goto err_lookup; + } ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); if (ret == STATUS_ERROR) { err = errno; From bb0168e56baa1d7e33eec49ee36866eb2f7806bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:33:41 +0300 Subject: [PATCH 12/52] Rejected updates of uninitialized MFTMirr Reject updating uninitialized MFTMirr because it leads to endless recursions as updating the MFTMirr entry of MFT implies further updating MFTMirr. This is probably specific to ntfsfix, as mounts are normally rejected when MFTMirr does not match MFT. --- libntfs-3g/mft.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index a80d1e4e..33b832a9 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -172,6 +172,15 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, cnt = vol->mftmirr_size - m; if (cnt > count) cnt = count; + if ((m + cnt) > vol->mftmirr_na->initialized_size >> + vol->mft_record_size_bits) { + errno = ESPIPE; + ntfs_log_perror("Trying to write non-allocated mftmirr" + " records (%lld > %lld)", (long long)m + cnt, + (long long)vol->mftmirr_na->initialized_size >> + vol->mft_record_size_bits); + return -1; + } bmirr = ntfs_malloc(cnt * vol->mft_record_size); if (!bmirr) return -1; From 8adc9c618496d2c99b0bdc5bd7db6d59a86c513b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:35:02 +0300 Subject: [PATCH 13/52] Hardened consistency checks of MFT records Check the record is properly aligned and does not flow beyond the expected end of the record. --- libntfs-3g/mft.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 33b832a9..267f7306 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -240,7 +240,19 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, le32_to_cpu(m->bytes_allocated)); goto err_out; } - + if (le32_to_cpu(m->bytes_in_use) > vol->mft_record_size) { + ntfs_log_error("Record %llu has corrupt in-use size " + "(%u > %u)\n", (unsigned long long)MREF(mref), + (int)le32_to_cpu(m->bytes_in_use), + (int)vol->mft_record_size); + goto err_out; + } + if (le16_to_cpu(m->attrs_offset) & 7) { + ntfs_log_error("Attributes badly aligned in record %llu\n", + (unsigned long long)MREF(mref)); + goto err_out; + } + a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset)); if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) { ntfs_log_error("Record %llu is corrupt\n", From 5bdfac970413fa1331ad938cf562bed0e343f121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:35:51 +0300 Subject: [PATCH 14/52] Added generic checks of MFT records in ntfsfix Before acting on MFT, make sure it passes the consistency check. --- ntfsprogs/ntfsfix.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c index 6801e70e..48e8a782 100644 --- a/ntfsprogs/ntfsfix.c +++ b/ntfsprogs/ntfsfix.c @@ -823,7 +823,8 @@ static BOOL short_mft_selfloc_condition(struct MFT_SELF_LOCATED *selfloc) vol->mft_record_size, mft0) == vol->mft_record_size) && !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft0, - vol->mft_record_size)) { + vol->mft_record_size) + && !ntfs_mft_record_check(vol, 0, mft0)) { a = find_unnamed_attr(mft0,AT_DATA); if (a && a->non_resident @@ -961,7 +962,9 @@ static BOOL self_mapped_selfloc_condition(struct MFT_SELF_LOCATED *selfloc) if ((ntfs_pread(vol->dev, offs, vol->mft_record_size, mft1) == vol->mft_record_size) && !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft1, - vol->mft_record_size)) { + vol->mft_record_size) + && !ntfs_mft_record_check(vol, inum, mft1)) { + lowest_vcn = (SELFLOC_LIMIT*vol->mft_record_size) >> vol->cluster_size_bits; a = find_unnamed_attr(mft1,AT_DATA); @@ -1017,7 +1020,8 @@ static BOOL spare_record_selfloc_condition(struct MFT_SELF_LOCATED *selfloc) if ((ntfs_pread(vol->dev, offs, vol->mft_record_size, mft2) == vol->mft_record_size) && !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft2, - vol->mft_record_size)) { + vol->mft_record_size) + && !ntfs_mft_record_check(vol, inum, mft2)) { if (!mft2->base_mft_record && (mft2->flags & MFT_RECORD_IN_USE) && !find_unnamed_attr(mft2,AT_ATTRIBUTE_LIST) From d60175230ad97a01df7862c09b392f73e9db4a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:36:58 +0300 Subject: [PATCH 15/52] Added consistency check of MFT attributes in ntfsfix Make sure the searched attribute is fully within the MFT record and is correctly aligned. --- ntfsprogs/ntfsfix.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c index 48e8a782..da92fadc 100644 --- a/ntfsprogs/ntfsfix.c +++ b/ntfsprogs/ntfsfix.c @@ -784,7 +784,9 @@ static ATTR_RECORD *find_unnamed_attr(MFT_RECORD *mrec, ATTR_TYPES type) /* fetch the requested attribute */ offset = le16_to_cpu(mrec->attrs_offset); a = (ATTR_RECORD*)((char*)mrec + offset); - while ((offset < le32_to_cpu(mrec->bytes_in_use)) + while (((offset + le32_to_cpu(a->length)) + < le32_to_cpu(mrec->bytes_in_use)) + && !(le32_to_cpu(a->length) & 7) && (a->type != AT_END) && ((a->type != type) || a->name_length)) { offset += le32_to_cpu(a->length); From 02e199978b0af3a2cb7b5929bceaf19127ea95eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:37:45 +0300 Subject: [PATCH 16/52] Fixed initialization of a Posix ACL A posix ACL record was not fully initialized. --- libntfs-3g/acls.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libntfs-3g/acls.c b/libntfs-3g/acls.c index 9df71083..9f16fecd 100644 --- a/libntfs-3g/acls.c +++ b/libntfs-3g/acls.c @@ -1330,6 +1330,10 @@ struct POSIX_SECURITY *ntfs_build_basic_posix( pydesc->acccnt = 3; pydesc->defcnt = 0; pydesc->firstdef = 6; + pydesc->filler = 0; + pydesc->acl.version = POSIX_VERSION; + pydesc->acl.flags = 0; + pydesc->acl.filler = 0; } else errno = ENOMEM; return (pydesc); From 218d93b37b7d1917598489d0ffc8d32bf34c7c56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:38:20 +0300 Subject: [PATCH 17/52] Cleaned the tail of compressed buffer before writing a full cluster When appending to an existing compressed file, the last block must first be decompressed. When doing so, clear the tail of buffer in order to not leak unrelated data. --- libntfs-3g/compress.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/compress.c b/libntfs-3g/compress.c index fb69a0e6..0c22bc9c 100644 --- a/libntfs-3g/compress.c +++ b/libntfs-3g/compress.c @@ -1618,7 +1618,7 @@ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, */ static s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, - const char *outbuf, s32 count, BOOL compress, + char *outbuf, s32 count, BOOL compress, BOOL appending, VCN *update_from) { s32 rounded; @@ -1639,6 +1639,8 @@ static s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, if (!compress) { clsz = 1 << na->ni->vol->cluster_size_bits; rounded = ((count - 1) | (clsz - 1)) + 1; + if (rounded > count) + memset(&outbuf[count], 0, rounded - count); written = write_clusters(na->ni->vol, rl, offs, rounded, outbuf); if (written != rounded) From 37a330ea8c788db312c4fbfe03ce5f1f7d53bd7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:39:13 +0300 Subject: [PATCH 18/52] Improved the consistency checks of standard information Make sure the standard information attribute has a valid size. --- libntfs-3g/inode.c | 20 +++++++++++++------- libntfs-3g/volume.c | 7 +++++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index b25f4051..fe57efaa 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -189,6 +189,13 @@ static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref) " %lld", (long long)MREF(mref)); goto put_err_out; } + lthle = ctx->attr->value_length; + if (le32_to_cpu(lthle) < offsetof(STANDARD_INFORMATION, owner_id)) { + ntfs_log_error("Corrupt STANDARD_INFORMATION in base" + " record %lld\n", + (long long)MREF(mref)); + goto put_err_out; + } std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); ni->flags = std_info->file_attributes; @@ -196,10 +203,9 @@ static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref) ni->last_data_change_time = std_info->last_data_change_time; ni->last_mft_change_time = std_info->last_mft_change_time; ni->last_access_time = std_info->last_access_time; - /* JPA insert v3 extensions if present */ - /* length may be seen as 72 (v1.x) or 96 (v3.x) */ - lthle = ctx->attr->length; - if (le32_to_cpu(lthle) > sizeof(STANDARD_INFORMATION)) { + /* Insert v3 extensions if present */ + /* length may be seen as 48 (v1.x) or 72 (v3.x) */ + if (le32_to_cpu(lthle) >= offsetof(STANDARD_INFORMATION, v3_end)) { set_nino_flag(ni, v3_Extensions); ni->owner_id = std_info->owner_id; ni->security_id = std_info->security_id; @@ -760,13 +766,13 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni) /* JPA update v3.x extensions, ensuring consistency */ - lthle = ctx->attr->length; + lthle = ctx->attr->value_length; lth = le32_to_cpu(lthle); if (test_nino_flag(ni, v3_Extensions) - && (lth <= sizeof(STANDARD_INFORMATION))) + && (lth < offsetof(STANDARD_INFORMATION, v3_end))) ntfs_log_error("bad sync of standard information\n"); - if (lth > sizeof(STANDARD_INFORMATION)) { + if (lth >= offsetof(STANDARD_INFORMATION, v3_end)) { std_info->owner_id = ni->owner_id; std_info->security_id = ni->security_id; std_info->quota_charged = ni->quota_charged; diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index c6356563..e1c93236 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -224,10 +224,13 @@ static int __ntfs_volume_release(ntfs_volume *v) static void ntfs_attr_setup_flag(ntfs_inode *ni) { STANDARD_INFORMATION *si; + s64 lth; - si = ntfs_attr_readall(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, NULL); + si = (STANDARD_INFORMATION*)ntfs_attr_readall(ni, + AT_STANDARD_INFORMATION, AT_UNNAMED, 0, <h); if (si) { - ni->flags = si->file_attributes; + if ((u64)lth >= offsetof(STANDARD_INFORMATION, owner_id)) + ni->flags = si->file_attributes; free(si); } } From d86c34390dcdc0be72093f9524d2c2f619b51fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:39:52 +0300 Subject: [PATCH 19/52] Added a consistency check of attribute size in readall() Make sure the attribute designated to readall() has a reasonable size. Apart from a bitmap, in a valid call, the attribute size is limited to 65536, and bitmaps are limited by the number of cluster. --- libntfs-3g/attrib.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 89af840d..6fbb9a18 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -6727,6 +6727,19 @@ void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, ntfs_log_perror("ntfs_attr_open failed, inode %lld attr 0x%lx", (long long)ni->mft_no,(long)le32_to_cpu(type)); goto err_exit; + } + /* + * Consistency check : restrict to 65536 bytes. + * index bitmaps may need more, but still limited by + * the number of clusters. + */ + if ((na->data_size > 65536) + && ((type != AT_BITMAP) + || ((na->data_size << 3) > ni->vol->nr_clusters))) { + ntfs_log_error("Corrupt attribute 0x%lx in inode %lld\n", + (long)le32_to_cpu(type),(long long)ni->mft_no); + errno = EOVERFLOW; + goto out; } data = ntfs_malloc(na->data_size); if (!data) From a4177d625c9a5e7925dfa5422a673a12c936c3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 17 May 2021 15:42:31 +0300 Subject: [PATCH 20/52] Made sure an error code is issued for failed mounts In case of failure to mount a file system, an error code is expected to indicate the failure reason. In an unclear case the error code was not set, so force an EIO when this happens. --- src/lowntfs-3g.c | 5 ++++- src/ntfs-3g.c | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index efc1a036..cfa61626 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4368,9 +4368,12 @@ static int ntfs_open(const char *device) } errno = 0; + goto out; err_out: + if (!errno) /* Make sure to return an error */ + errno = EIO; +out : return ntfs_volume_error(errno); - } static void usage(void) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index fffe5f38..1f148ba0 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4076,9 +4076,12 @@ static int ntfs_open(const char *device) } errno = 0; + goto out; err_out: + if (!errno) + errno = EIO; +out : return ntfs_volume_error(errno); - } static void usage(void) From fc295da55edc624eca83b65730bde8148b77d1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 19 May 2021 15:18:56 +0200 Subject: [PATCH 21/52] Configured for version 2021.5.19 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 7d8324f5..e2005cc5 100644 --- a/configure.ac +++ b/configure.ac @@ -24,8 +24,8 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2017.3.23],[ntfs-3g-devel@lists.sf.net]) -LIBNTFS_3G_VERSION="88" +AC_INIT([ntfs-3g],[2021.5.19],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="89" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) # Environment From f3999a74c68b26d04d6d34302d8d5dcb8e3ac56d 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 22/52] Fixed consistency check of MFT attributes in ntfsfix The consistency check could be defeated as a consequence of integer overflow. Reorganize it to avoid such situations. --- ntfsprogs/ntfsfix.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c index da92fadc..b214068f 100644 --- a/ntfsprogs/ntfsfix.c +++ b/ntfsprogs/ntfsfix.c @@ -780,16 +780,19 @@ static ATTR_RECORD *find_unnamed_attr(MFT_RECORD *mrec, ATTR_TYPES type) { ATTR_RECORD *a; u32 offset; + s32 space; /* fetch the requested attribute */ offset = le16_to_cpu(mrec->attrs_offset); + space = le32_to_cpu(mrec->bytes_in_use) - offset; a = (ATTR_RECORD*)((char*)mrec + offset); - while (((offset + le32_to_cpu(a->length)) - < le32_to_cpu(mrec->bytes_in_use)) - && !(le32_to_cpu(a->length) & 7) + while ((space >= (s32)offsetof(ATTR_RECORD, resident_end)) && (a->type != AT_END) + && (le32_to_cpu(a->length) <= (u32)space) + && !(le32_to_cpu(a->length) & 7) && ((a->type != type) || a->name_length)) { offset += le32_to_cpu(a->length); + space -= le32_to_cpu(a->length); a = (ATTR_RECORD*)((char*)mrec + offset); } if ((offset >= le32_to_cpu(mrec->bytes_in_use)) From 4e09b252af7ca75a278b33e5aab019f4c5c76fd8 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 23/52] Checked name limits while looking up for an external attribute Make sure the name of an attribute does not overflow beyond the attribute length. --- libntfs-3g/attrib.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 6fbb9a18..83a2d603 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2835,6 +2835,16 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, } } else { register int rc; + + if (a->name_length + && ((le16_to_cpu(a->name_offset) + + a->name_length * sizeof(ntfschar)) + > le32_to_cpu(a->length))) { + ntfs_log_error("Corrupt attribute name" + " in MFT record %lld\n", + (long long)ctx->ntfs_ino->mft_no); + break; + } if (name && ((rc = ntfs_names_full_collate(name, name_len, (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)), 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 24/52] 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; } From 4e094563d4989ae4451fb61ea0584986f6a711cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 25/52] Checked $MFT data to be ready before fetching an extent --- libntfs-3g/attrib.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 12989615..799f2ee4 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3217,6 +3217,12 @@ is_enumeration: ctx->mrec = ctx->base_mrec; } else { /* We want an extent record. */ + if (!vol->mft_na) { + ntfs_log_perror("$MFT not ready for " + "opening an extent to inode %lld\n", + (long long)base_ni->mft_no); + break; + } ni = ntfs_extent_inode_open(base_ni, al_entry->mft_reference); if (!ni) From ec7bf5437738fa56c2e259521e3a18c972ac76be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 26/52] Checked the MFTMirr lcn to be consistent with the boot sector The lcn of the initial run of the MFTMirr data must be the same as described in the boot sector. As a consequence this cannot be a hole. --- libntfs-3g/volume.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index e6ec498b..a6551c78 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -458,6 +458,12 @@ static int ntfs_mftmirr_load(ntfs_volume *vol) ntfs_log_perror("Failed to map runlist of $MFTMirr/$DATA"); goto error_exit; } + if (vol->mftmirr_na->rl->lcn != vol->mftmirr_lcn) { + ntfs_log_error("Bad $MFTMirr lcn 0x%llx, want 0x%llx\n", + (long long)vol->mftmirr_na->rl->lcn, + (long long)vol->mftmirr_lcn); + goto error_exit; + } return 0; From 84478c401236f3e4345174ea2b71f578aa45335d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 27/52] Checked attributes layout against length actually used The end of the attributes must be checked against the space in use rather than the allocated space. This contributes to the safety of subsequent attribute resizing. --- libntfs-3g/attrib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 799f2ee4..caf0f4a0 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -2801,7 +2801,7 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, * and we can safely access its minimal fields. */ offs = p2n(a) - p2n(ctx->mrec); - space = le32_to_cpu(ctx->mrec->bytes_allocated) - offs; + space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs; if ((offs < 0) || (((space < (ptrdiff_t)offsetof(ATTR_RECORD, resident_end)) @@ -3256,7 +3256,7 @@ do_next_attr_loop: * and we can safely access its minimal fields. */ offs = p2n(a) - p2n(ctx->mrec); - space = le32_to_cpu(ctx->mrec->bytes_allocated) - offs; + space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs; if (offs < 0) break; if ((space >= 4) && (a->type == AT_END)) From 68ead9b8fc5b1d466fca7ef4c487b8d5b8996d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 28/52] Checked expanded attribute does not overflow the space in use When resizing an attribute, make sure the trailing data is moved in the correct direction. --- libntfs-3g/attrib.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index caf0f4a0..387ccde5 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -4635,6 +4635,13 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) } /* Move attributes following @a to their new location. */ + if (((u8 *)m + old_size) < ((u8 *)a + attr_size)) { + ntfs_log_error("Attribute 0x%x overflows" + " from MFT record\n", + (int)le32_to_cpu(a->type)); + errno = EIO; + return (-1); + } memmove((u8 *)a + new_size, (u8 *)a + attr_size, old_size - ((u8 *)a - (u8 *)m) - attr_size); From 32e858a87a4184a05a1b54315c06ac94488770b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 29/52] Aborted mounting when cannot access standard information of MFT The standard information of the MFT must be its first attribute in the base record. If it is not accessible initially, we end up searching it in an extent before the MFT struct is ready for that. --- libntfs-3g/volume.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index a6551c78..92743735 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -221,10 +221,11 @@ static int __ntfs_volume_release(ntfs_volume *v) return errno ? -1 : 0; } -static void ntfs_attr_setup_flag(ntfs_inode *ni) +static int ntfs_attr_setup_flag(ntfs_inode *ni) { STANDARD_INFORMATION *si; s64 lth; + int r; si = (STANDARD_INFORMATION*)ntfs_attr_readall(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, <h); @@ -232,7 +233,12 @@ static void ntfs_attr_setup_flag(ntfs_inode *ni) if ((u64)lth >= offsetof(STANDARD_INFORMATION, owner_id)) ni->flags = si->file_attributes; free(si); + r = 0; + } else { + ntfs_log_error("Failed to get standard information of $MFT\n"); + r = -1; } + return (r); } /** @@ -317,7 +323,8 @@ static int ntfs_mft_load(ntfs_volume *vol) mft_has_no_attr_list: - ntfs_attr_setup_flag(vol->mft_ni); + if (ntfs_attr_setup_flag(vol->mft_ni)) + goto error_exit; /* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */ From 436fe09f87ccd92cd037b8a5547340ee01b58e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 30/52] Checked consistency of index entries Make sure the data and key in indexes do not overflow from index entries --- include/ntfs-3g/index.h | 2 ++ libntfs-3g/dir.c | 27 ++++++++++++++---- libntfs-3g/index.c | 62 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 6 deletions(-) diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index 036b742f..4e3f73f7 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -139,6 +139,8 @@ extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, extern void ntfs_index_ctx_put(ntfs_index_context *ictx); extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx); +extern int ntfs_index_entry_consistent(const INDEX_ENTRY *ie, + COLLATION_RULES collation_rule, u64 inum); extern int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ictx) __attribute_warn_unused_result__; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index f3c14d8f..b2afbc34 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -328,9 +328,10 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, if (ie->ie_flags & INDEX_ENTRY_END) break; - if (!le16_to_cpu(ie->length)) { - ntfs_log_error("Zero length index entry in inode %lld" - "\n", (unsigned long long)dir_ni->mft_no); + /* The file name must not overflow from the entry */ + if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + dir_ni->mft_no)) { + errno = EIO; goto put_err_out; } /* @@ -467,10 +468,10 @@ descend_into_child_node: if (ie->ie_flags & INDEX_ENTRY_END) break; - if (!le16_to_cpu(ie->length)) { + /* The file name must not overflow from the entry */ + if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + dir_ni->mft_no)) { errno = EIO; - ntfs_log_error("Zero length index entry in inode %lld" - "\n", (unsigned long long)dir_ni->mft_no); goto close_err_out; } /* @@ -1273,6 +1274,13 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, /* Skip index root entry if continuing previous readdir. */ if (ir_pos > (u8*)ie - (u8*)ir) continue; + + /* The file name must not overflow from the entry */ + if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + dir_ni->mft_no)) { + errno = EIO; + goto dir_err_out; + } /* * Submit the directory entry to ntfs_filldir(), which will * invoke the filldir() callback as appropriate. @@ -1429,6 +1437,13 @@ find_next_index_buffer: /* Skip index entry if continuing previous readdir. */ if (ia_pos - ia_start > (u8*)ie - (u8*)ia) continue; + + /* The file name must not overflow from the entry */ + if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + dir_ni->mft_no)) { + errno = EIO; + goto dir_err_out; + } /* * Submit the directory entry to ntfs_filldir(), which will * invoke the filldir() callback as appropriate. diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index e12cbafd..1bd0acd5 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -469,6 +469,57 @@ static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len) return ir; } +/* + * Check the consistency of an index entry + * + * Make sure data and key do not overflow from entry. + * As a side effect, an entry with zero length is rejected. + * This entry must be a full one (no INDEX_ENTRY_END flag), and its + * length must have been checked beforehand to not overflow from the + * index record. + * + * Returns 0 if no error was found + * -1 otherwise (with errno unchanged) + */ + +int ntfs_index_entry_consistent(const INDEX_ENTRY *ie, + COLLATION_RULES collation_rule, u64 inum) +{ + int ret; + + ret = 0; + if (ie->key_length + && ((le16_to_cpu(ie->key_length) + offsetof(INDEX_ENTRY, key)) + > le16_to_cpu(ie->length))) { + ntfs_log_error("Overflow from index entry in inode %lld\n", + (long long)inum); + ret = -1; + + } else + if (collation_rule == COLLATION_FILE_NAME) { + if ((offsetof(INDEX_ENTRY, key.file_name.file_name) + + ie->key.file_name.file_name_length + * sizeof(ntfschar)) + > le16_to_cpu(ie->length)) { + ntfs_log_error("File name overflow from index" + " entry in inode %lld\n", + (long long)inum); + ret = -1; + } + } else { + if (ie->data_length + && ((le16_to_cpu(ie->data_offset) + + le16_to_cpu(ie->data_length)) + > le16_to_cpu(ie->length))) { + ntfs_log_error("Data overflow from index" + " entry in inode %lld\n", + (long long)inum); + ret = -1; + } + } + return (ret); +} + /** * Find a key in the index block. * @@ -521,6 +572,12 @@ static int ntfs_ie_lookup(const void *key, const int key_len, ntfs_log_error("Collation function not defined\n"); errno = EOPNOTSUPP; return STATUS_ERROR; + } + /* Make sure key and data do not overflow from entry */ + if (ntfs_index_entry_consistent(ie, icx->ir->collation_rule, + icx->ni->mft_no)) { + errno = EIO; + return STATUS_ERROR; } rc = icx->collate(icx->ni->vol, key, key_len, &ie->key, le16_to_cpu(ie->key_length)); @@ -704,6 +761,7 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic else icx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS; /* get the appropriate collation function */ + icx->ir = ir; icx->collate = ntfs_get_collate_function(ir->collation_rule); if (!icx->collate) { err = errno = EOPNOTSUPP; @@ -790,6 +848,10 @@ descend_into_child_node: err_out: icx->bad_index = TRUE; /* Force icx->* to be freed */ err_lookup: + if (icx->actx) { + ntfs_attr_put_search_ctx(icx->actx); + icx->actx = NULL; + } free(ib); if (!err) err = EIO; From 7f45544ed7bdbcedd344dd960a6cd4797f66b43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 31/52] Added and grouped generic attribute checks Checked that attributes are [non-]resident when they have to be, and grouped consistency checks on each of them in a dedicated function. Consequenly request the checks where needed and remove existing index checks. --- include/ntfs-3g/attrib.h | 1 + libntfs-3g/attrib.c | 209 ++++++++++++++++++++++++++++++--------- libntfs-3g/dir.c | 17 +--- libntfs-3g/index.c | 12 --- libntfs-3g/mft.c | 49 ++++++++- libntfs-3g/volume.c | 8 ++ 6 files changed, 222 insertions(+), 74 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index b3752a60..e2cf41b3 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -398,6 +398,7 @@ extern int ntfs_attr_data_write(ntfs_inode *ni, const char *buf, size_t size, off_t offset); extern int ntfs_attr_shrink_size(ntfs_inode *ni, ntfschar *stream_name, int stream_name_len, off_t offset); +extern int ntfs_attr_consistent(const ATTR_RECORD *a, const MFT_REF mref); #endif /* defined _NTFS_ATTRIB_H */ diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 387ccde5..66c1727d 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3356,6 +3356,170 @@ not_found: } } +/* + * Check the consistency of an attribute + * + * Do the general consistency checks of the selected attribute : + * - the required fields can be accessed + * - the variable fields do not overflow + * - the attribute is [non-]resident if it must be + * - miscelleaneous checks + * + * Returns 0 if the checks pass + * -1 with errno = EIO otherwise + */ + +int ntfs_attr_consistent(const ATTR_RECORD *a, const MFT_REF mref) +{ + const FILE_NAME_ATTR *fn; + const INDEX_ROOT *ir; + u64 inum; + int ret; + + /* + * The attribute was found to fully lie within the MFT + * record, now make sure its relevant parts (name, runlist, + * value) also lie within. The first step is to make sure + * the attribute has the minimum length so that accesses to + * the lengths and offsets of these parts are safe. + */ + ret = 0; + inum = MREF(mref); + if (a->non_resident) { + if ((a->non_resident != 1) + || (le32_to_cpu(a->length) + < offsetof(ATTR_RECORD, non_resident_end)) + || (le16_to_cpu(a->mapping_pairs_offset) + >= le32_to_cpu(a->length)) + || (a->name_length + && (((u32)le16_to_cpu(a->name_offset) + + a->name_length * sizeof(ntfschar)) + > le32_to_cpu(a->length))) + || (le64_to_cpu(a->highest_vcn) + < le64_to_cpu(a->lowest_vcn))) { + ntfs_log_error("Corrupt non resident attribute" + " 0x%x in MFT record %lld\n", + (int)le32_to_cpu(a->type), + (long long)inum); + errno = EIO; + ret = -1; + } + } else { + if ((le32_to_cpu(a->length) + < offsetof(ATTR_RECORD, resident_end)) + || (le32_to_cpu(a->value_length) & 0xff000000) + || (a->value_length + && ((le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length)) + > le32_to_cpu(a->length))) + || (a->name_length + && (((u32)le16_to_cpu(a->name_offset) + + a->name_length * sizeof(ntfschar)) + > le32_to_cpu(a->length)))) { + ntfs_log_error("Corrupt resident attribute" + " 0x%x in MFT record %lld\n", + (int)le32_to_cpu(a->type), + (long long)inum); + errno = EIO; + ret = -1; + } + } + if (!ret) { + /* + * Checking whether an attribute must be [non-]resident + * is hard-coded for well-known ones. This should be + * done through ntfs_attr_can_be_non_resident(), based on + * $AttrDef, but this would give an easy way to bypass + * the checks. + * Attributes which are not well-known are not checked. + * + * Note : at this stage we know that a->length and + * a->value_length cannot look like being negative. + */ + switch(a->type) { + case AT_FILE_NAME : + /* Check file names are resident and do not overflow */ + fn = (const FILE_NAME_ATTR*)((const u8*)a + + le16_to_cpu(a->value_offset)); + if (a->non_resident + || (le32_to_cpu(a->value_length) + < offsetof(FILE_NAME_ATTR, file_name)) + || !fn->file_name_length + || ((fn->file_name_length * sizeof(ntfschar) + + offsetof(FILE_NAME_ATTR, file_name)) + > le32_to_cpu(a->value_length))) { + ntfs_log_error("Corrupt file name" + " attribute in MFT record %lld.\n", + (long long)inum); + errno = EIO; + ret = -1; + } + break; + case AT_INDEX_ROOT : + /* Check root index is resident and does not overflow */ + ir = (const INDEX_ROOT*)((const u8*)a + + le16_to_cpu(a->value_offset)); + /* index.allocated_size may overflow while resizing */ + if (a->non_resident + || (le32_to_cpu(a->value_length) + < offsetof(INDEX_ROOT, index.reserved)) + || (le32_to_cpu(ir->index.index_length) + & 0xff000000) + || ((le32_to_cpu(a->value_length) + - le32_to_cpu(ir->index.index_length)) + < offsetof(INDEX_ROOT,index)) + || ((le32_to_cpu(a->value_length) + - le32_to_cpu(ir->index.index_length)) + < le32_to_cpu(ir->index.entries_offset)) + || (le32_to_cpu(ir->index.index_length) + > le32_to_cpu(ir->index.allocated_size))) { + ntfs_log_error("Corrupt index root" + " in MFT record %lld.\n", + (long long)inum); + errno = EIO; + ret = -1; + } + break; + case AT_STANDARD_INFORMATION : + case AT_OBJECT_ID : + case AT_VOLUME_NAME : + case AT_EA_INFORMATION : + if (a->non_resident) { + ntfs_log_error("Attribute 0x%x in MFT record" + " %lld should be resident.\n", + (int)le32_to_cpu(a->type), + (long long)inum); + errno = EIO; + ret = -1; + } + break; + case AT_VOLUME_INFORMATION : + if (a->non_resident + || (le32_to_cpu(a->value_length) + < sizeof(VOLUME_INFORMATION))) { + ntfs_log_error("Corrupt volume information" + " in MFT record %lld\n", + (long long)inum); + errno = EIO; + ret = -1; + } + break; + case AT_INDEX_ALLOCATION : + if (!a->non_resident) { + ntfs_log_error("Corrupt index allocation" + " in MFT record %lld", + (long long)inum); + errno = EIO; + ret = -1; + } + break; + default : + break; + } + } + return (ret); +} + /** * ntfs_attr_lookup - find an attribute in an ntfs inode * @type: attribute type to find @@ -3430,7 +3594,6 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, { ntfs_volume *vol; ntfs_inode *base_ni; - ATTR_RECORD *a; int ret = -1; ntfs_log_enter("Entering for attribute type 0x%x\n", le32_to_cpu(type)); @@ -3452,50 +3615,6 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, else ret = ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn, val, val_len, ctx); - if (!ret) { - /* - * The attribute was found to fully lie within the MFT - * record, now make sure its relevant parts (name, runlist, - * value) also lie within. The first step is to make sure - * the attribute has the minimum length so that accesses to - * the lengths and offsets of these parts are safe. - */ - a = ctx->attr; - if (a->non_resident) { - if ((le32_to_cpu(a->length) - < offsetof(ATTR_RECORD, non_resident_end)) - || (le16_to_cpu(a->mapping_pairs_offset) - >= le32_to_cpu(a->length)) - || (a->name_length - && ((le16_to_cpu(a->name_offset) - + a->name_length) - > le32_to_cpu(a->length)))) { - ntfs_log_error("Corrupt non resident attribute" - " 0x%x in MFT record %lld\n", - (int)le32_to_cpu(a->type), - (long long)ctx->ntfs_ino->mft_no); - errno = EIO; - ret = -1; - } - } else { - if ((le32_to_cpu(a->length) - < offsetof(ATTR_RECORD, resident_end)) - || (le16_to_cpu(a->value_offset) - + le32_to_cpu(a->value_length)) - > le32_to_cpu(a->length) - || (a->name_length - && ((le16_to_cpu(a->name_offset) - + a->name_length) - > le32_to_cpu(a->length)))) { - ntfs_log_error("Corrupt resident attribute 0x%x in" - " MFT record %lld\n", - (int)le32_to_cpu(a->type), - (long long)ctx->ntfs_ino->mft_no); - errno = EIO; - ret = -1; - } - } - } out: ntfs_log_leave("\n"); return ret; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index b2afbc34..bee6fbbe 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -293,16 +293,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, (unsigned)index_block_size); goto put_err_out; } - if (((offsetof(INDEX_ROOT,index) - + le32_to_cpu(ir->index.allocated_size)) - > le32_to_cpu(ctx->attr->value_length)) - || (le32_to_cpu(ir->index.entries_offset) - > le32_to_cpu(ir->index.index_length)) - || (le32_to_cpu(ir->index.index_length) - > le32_to_cpu(ir->index.allocated_size))) { - ntfs_log_error("Index root is corrupt.\n"); - goto put_err_out; - } + /* Consistency check of ir done while fetching attribute */ index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); /* The first index entry. */ ie = (INDEX_ENTRY*)((u8*)&ir->index + @@ -1097,12 +1088,6 @@ static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) } fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); - if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) > - (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) { - ntfs_log_error("Corrupt file name attribute in inode %lld.\n", - (unsigned long long)ni->mft_no); - goto io_err_out; - } mref = le64_to_cpu(fn->parent_directory); ntfs_attr_put_search_ctx(ctx); return mref; diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 1bd0acd5..f804efcc 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -730,7 +730,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_BLOCK *ib = NULL; - ATTR_RECORD *a; int ret, err = 0; ntfs_log_trace("Entering\n"); @@ -771,17 +770,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic } old_vcn = VCN_INDEX_ROOT_PARENT; - a = icx->actx->attr; - if (((offsetof(INDEX_ROOT,index) - + le32_to_cpu(ir->index.index_length)) - > le32_to_cpu(a->value_length)) - || (le32_to_cpu(ir->index.entries_offset) - > le32_to_cpu(ir->index.index_length))) { - ntfs_log_error("Index root is corrupt in MFT record %lld.\n", - (long long)icx->ni->mft_no); - err = errno = ERANGE; - goto err_lookup; - } ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); if (ret == STATUS_ERROR) { err = errno; diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 267f7306..cc244871 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -219,11 +219,26 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, return -1; } +/* + * Check the consistency of an MFT record + * + * Make sure its general fields are safe, then examine all its + * attributes and apply generic checks to them. + * The attribute checks are skipped when a record is being read in + * order to collect its sequence number for creating a new record. + * + * Returns 0 if the checks are successful + * -1 with errno = EIO otherwise + */ + int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD *m) { ATTR_RECORD *a; + ATTR_TYPES previous_type; int ret = -1; + u32 offset; + s32 space; if (!ntfs_is_file_record(m->magic)) { if (!NVolNoFixupWarn(vol)) @@ -240,7 +255,8 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, le32_to_cpu(m->bytes_allocated)); goto err_out; } - if (le32_to_cpu(m->bytes_in_use) > vol->mft_record_size) { + if (!NVolNoFixupWarn(vol) + && (le32_to_cpu(m->bytes_in_use) > vol->mft_record_size)) { ntfs_log_error("Record %llu has corrupt in-use size " "(%u > %u)\n", (unsigned long long)MREF(mref), (int)le32_to_cpu(m->bytes_in_use), @@ -259,6 +275,37 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, (unsigned long long)MREF(mref)); goto err_out; } + + if (!NVolNoFixupWarn(vol)) { + offset = le16_to_cpu(m->attrs_offset); + space = le32_to_cpu(m->bytes_in_use) - offset; + a = (ATTR_RECORD*)((char*)m + offset); + previous_type = AT_STANDARD_INFORMATION; + while ((space >= (s32)offsetof(ATTR_RECORD, resident_end)) + && (a->type != AT_END) + && (le32_to_cpu(a->type) >= le32_to_cpu(previous_type))) { + if ((le32_to_cpu(a->length) <= (u32)space) + && !(le32_to_cpu(a->length) & 7)) { + if (!ntfs_attr_consistent(a, mref)) { + previous_type = a->type; + offset += le32_to_cpu(a->length); + space -= le32_to_cpu(a->length); + a = (ATTR_RECORD*)((char*)m + offset); + } else + goto err_out; + } else { + ntfs_log_error("Corrupted MFT record %llu\n", + (unsigned long long)MREF(mref)); + goto err_out; + } + } + /* We are supposed to reach an AT_END */ + if ((space < 4) || (a->type != AT_END)) { + ntfs_log_error("Bad end of MFT record %llu\n", + (unsigned long long)MREF(mref)); + goto err_out; + } + } ret = 0; err_out: diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 92743735..e538fa5a 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -976,6 +976,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) } goto error_exit; } + for (i = 0; (i < l) && (i < FILE_first_user); ++i) + if (ntfs_mft_record_check(vol, FILE_MFT + i, + (MFT_RECORD*)(m + i*vol->mft_record_size))) + goto error_exit; l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, vol->mft_record_size, m2); if (l != vol->mftmirr_size) { @@ -985,6 +989,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) } vol->mftmirr_size = l; } + for (i = 0; (i < l) && (i < FILE_first_user); ++i) + if (ntfs_mft_record_check(vol, FILE_MFT + i, + (MFT_RECORD*)(m2 + i*vol->mft_record_size))) + goto error_exit; ntfs_log_debug("Comparing $MFTMirr to $MFT...\n"); /* Windows 10 does not update the full $MFTMirr any more */ for (i = 0; (i < vol->mftmirr_size) && (i < FILE_first_user); ++i) { From af1bc0f5ec6db13e8d3fb289bc539e9f08e20e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 32/52] Hardened the check of locations of MFT and MFTMirr The MFT and MFTMirr may not be negative or overlap the boot sector. --- libntfs-3g/bootsect.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/bootsect.c b/libntfs-3g/bootsect.c index 483473e6..279247d9 100644 --- a/libntfs-3g/bootsect.c +++ b/libntfs-3g/bootsect.c @@ -155,7 +155,9 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) } /* MFT and MFTMirr may not overlap the boot sector or be the same */ - if (!b->mft_lcn || !b->mftmirr_lcn || (b->mft_lcn == b->mftmirr_lcn)) { + if (((s64)sle64_to_cpu(b->mft_lcn) <= 0) + || ((s64)sle64_to_cpu(b->mftmirr_lcn) <= 0) + || (b->mft_lcn == b->mftmirr_lcn)) { ntfs_log_error("Invalid location of MFT or MFTMirr.\n"); goto not_ntfs; } From f30b52490f16134b70d59a0d9e37716b62e38a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 33/52] Restricted the attribute definition table size to 24 bits The standard size is 2560 bytes. It can be extended for specific purposes, but its former limit to 32 bits was unreasonable. Anyway ntfs-3g is not committed to support non-standard situations. --- libntfs-3g/volume.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index e538fa5a..d1a5f2f0 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -1248,10 +1248,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) ntfs_log_perror("Failed to open ntfs attribute"); goto error_exit; } - /* Check we don't overflow 32-bits. */ - if (na->data_size > 0xffffffffLL) { + /* Check we don't overflow 24-bits. */ + if ((u64)na->data_size > 0xffffffLL) { ntfs_log_error("Attribute definition table is too big (max " - "32-bit allowed).\n"); + "24-bit allowed).\n"); errno = EINVAL; goto error_exit; } From 20d700841b73579107103dfc6a2f9f83acd4ffc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 34/52] Shown in log the inode of directory read error The unreadable directory record was poorly identified --- libntfs-3g/dir.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index bee6fbbe..0a3c87dd 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -400,8 +400,9 @@ descend_into_child_node: if (br != 1) { if (br != -1) errno = EIO; - ntfs_log_perror("Failed to read vcn 0x%llx", - (unsigned long long)vcn); + ntfs_log_perror("Failed to read vcn 0x%llx from inode %lld", + (unsigned long long)vcn, + (unsigned long long)ia_na->ni->mft_no); goto close_err_out; } From 5c002438f27d3dec445fa72c8089ffef7af5a51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 35/52] Checked that indexes do not exceed the index block size Make sure the used part of an index block fits into the allocated buffer. Note : a negative size may cause overflow on 32-bit cpus. (contributed by Rakesh Pandit) --- libntfs-3g/dir.c | 3 ++- libntfs-3g/index.c | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 0a3c87dd..76c052cf 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -425,7 +425,8 @@ descend_into_child_node: goto close_err_out; } index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); - if (index_end > (u8*)ia + index_block_size) { + if (((s32)le32_to_cpu(ia->index.index_length) < 0) + || (index_end > (u8*)ia + index_block_size)) { ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " "0x%llx exceeds maximum size.\n", (long long)vcn, (unsigned long long)dir_ni->mft_no); diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index f804efcc..c8c03481 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -421,6 +421,14 @@ static int ntfs_ia_check(ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn) icx->block_size); return -1; } + if (((s32)le32_to_cpu(ib->index.index_length) < 0) + || ((u8*)&ib->index + le32_to_cpu(ib->index.index_length) > + (u8*)ib + icx->block_size)) { + ntfs_log_error("Size of index buffer (%lld) of inode %llu " + "exceeds maximum size.\n", (long long)vcn, + (unsigned long long)icx->ni->mft_no); + return -1; + } return 0; } From 67f959df96136e2922100b8b585d92126438794f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 12 Jul 2021 08:31:18 +0200 Subject: [PATCH 36/52] Fixed the computation of the end of index entry The end of an index entry is related to its full length, not to the length of the key. Added an error message in an overflow case. --- libntfs-3g/dir.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 76c052cf..d4c8204e 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -306,10 +306,11 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, /* Bounds checks. */ if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || - (u8*)ie + le16_to_cpu(ie->key_length) > + (u8*)ie + le16_to_cpu(ie->length) > index_end) { - ntfs_log_error("Index entry out of bounds in inode %lld" - "\n", (unsigned long long)dir_ni->mft_no); + ntfs_log_error("Index root entry out of bounds in" + " inode %lld\n", + (unsigned long long)dir_ni->mft_no); goto put_err_out; } /* @@ -446,7 +447,7 @@ descend_into_child_node: /* Bounds check. */ if ((u8*)ie < (u8*)ia || (u8*)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || - (u8*)ie + le16_to_cpu(ie->key_length) > + (u8*)ie + le16_to_cpu(ie->length) > index_end) { ntfs_log_error("Index entry out of bounds in directory " "inode %lld.\n", @@ -1248,9 +1249,13 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, /* Bounds checks. */ if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || - (u8*)ie + le16_to_cpu(ie->key_length) > - index_end) + (u8*)ie + le16_to_cpu(ie->length) > + index_end) { + ntfs_log_error("Index root entry out of bounds in" + " inode %lld\n", + (unsigned long long)dir_ni->mft_no); goto dir_err_out; + } /* The last entry cannot contain a name. */ if (ie->ie_flags & INDEX_ENTRY_END) break; @@ -1408,7 +1413,7 @@ find_next_index_buffer: /* Bounds checks. */ if ((u8*)ie < (u8*)ia || (u8*)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || - (u8*)ie + le16_to_cpu(ie->key_length) > + (u8*)ie + le16_to_cpu(ie->length) > index_end) { ntfs_log_error("Index entry out of bounds in directory inode " "%lld.\n", (unsigned long long)dir_ni->mft_no); From f85ce6ff2e34464ceb68ea2d9766750b61512bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 37/52] Skipped errors for bad free clusters before they are computed The count of free clusters may be updated while mounting before it has been initialized, which may lead to irrelevant error messages. Moreover the count is not computed at all in some ntfsprogs utilities. So set up a flags to avoid outputting irrelevant errors. --- include/ntfs-3g/volume.h | 5 +++++ libntfs-3g/lcnalloc.c | 15 +++++++++------ libntfs-3g/volume.c | 4 +++- src/lowntfs-3g.c | 3 +-- src/ntfs-3g.c | 3 +-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index 30e07906..42800a28 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -117,6 +117,7 @@ typedef enum { NV_HideDotFiles, /* 1: Set hidden flag on dot files */ NV_Compression, /* 1: allow compression */ NV_NoFixupWarn, /* 1: Do not log fixup errors */ + NV_FreeSpaceKnown, /* 1: The free space is now known */ } ntfs_volume_state_bits; #define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) @@ -155,6 +156,10 @@ typedef enum { #define NVolSetNoFixupWarn(nv) set_nvol_flag(nv, NoFixupWarn) #define NVolClearNoFixupWarn(nv) clear_nvol_flag(nv, NoFixupWarn) +#define NVolFreeSpaceKnown(nv) test_nvol_flag(nv, FreeSpaceKnown) +#define NVolSetFreeSpaceKnown(nv) set_nvol_flag(nv, FreeSpaceKnown) +#define NVolClearFreeSpaceKnown(nv) clear_nvol_flag(nv, FreeSpaceKnown) + /* * NTFS version 1.1 and 1.2 are used by Windows NT4. * NTFS version 2.x is used by Windows 2000 Beta diff --git a/libntfs-3g/lcnalloc.c b/libntfs-3g/lcnalloc.c index 486e3510..a1c1ee33 100644 --- a/libntfs-3g/lcnalloc.c +++ b/libntfs-3g/lcnalloc.c @@ -368,12 +368,14 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, /* Allocate the bitmap bit. */ *byte |= bit; writeback = 1; - if (vol->free_clusters <= 0) - ntfs_log_error("Non-positive free clusters " - "(%lld)!\n", + if (NVolFreeSpaceKnown(vol)) { + if (vol->free_clusters <= 0) + ntfs_log_error("Non-positive free" + " clusters (%lld)!\n", (long long)vol->free_clusters); - else - vol->free_clusters--; + else + vol->free_clusters--; + } /* * Coalesce with previous run if adjacent LCNs. @@ -602,7 +604,8 @@ int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) ret = 0; out: vol->free_clusters += nr_freed; - if (vol->free_clusters > vol->nr_clusters) + if (NVolFreeSpaceKnown(vol) + && (vol->free_clusters > vol->nr_clusters)) ntfs_log_error("Too many free clusters (%lld > %lld)!", (long long)vol->free_clusters, (long long)vol->nr_clusters); diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index d1a5f2f0..2cc827bd 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -1894,8 +1894,10 @@ int ntfs_volume_get_free_space(ntfs_volume *vol) if (vol->free_mft_records < 0) ntfs_log_perror("Failed to calculate free MFT records"); - else + else { + NVolSetFreeSpaceKnown(vol); ret = 0; + } } return (ret); } diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index cfa61626..f28d7e0e 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4347,8 +4347,7 @@ static int ntfs_open(const char *device) if (ctx->ignore_case && ntfs_set_ignore_case(vol)) goto err_out; - vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na); - if (vol->free_clusters < 0) { + if (ntfs_volume_get_free_space(ctx->vol)) { ntfs_log_perror("Failed to read NTFS $Bitmap"); goto err_out; } diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 1f148ba0..5c07f23f 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4056,8 +4056,7 @@ static int ntfs_open(const char *device) !ctx->hide_hid_files, ctx->hide_dot_files)) goto err_out; - ctx->vol->free_clusters = ntfs_attr_get_free_bits(ctx->vol->lcnbmp_na); - if (ctx->vol->free_clusters < 0) { + if (ntfs_volume_get_free_space(ctx->vol)) { ntfs_log_perror("Failed to read NTFS $Bitmap"); goto err_out; } From 61134117c14302408e8a1520b6712ff2a3a41d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 38/52] Fixed checking the end of attrdef data Incomplete attribute definitions have to be rejected. --- libntfs-3g/attrib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 66c1727d..3cb696e1 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3767,8 +3767,9 @@ ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, ntfs_log_perror("%s: type=%d", __FUNCTION__, le32_to_cpu(type)); return NULL; } - for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef < - vol->attrdef_len && ad->type; ++ad) { + for (ad = vol->attrdef; ((ptrdiff_t)((u8*)ad - (u8*)vol->attrdef + + sizeof(ATTR_DEF)) <= vol->attrdef_len) + && ad->type; ++ad) { /* We haven't found it yet, carry on searching. */ if (le32_to_cpu(ad->type) < le32_to_cpu(type)) continue; From 55e7326350dd0002ee3058caa49399e2df0ce2ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 39/52] Avoided endless recursions when allocating the main bitmap Allocating clusters to the main bitmap may imply updating the bitmap itself within a cluster not yet allocated. This can turn into endless recursions and has to be rejected. Currently the bitmap is assumed to be fully allocated. --- libntfs-3g/attrib.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 3cb696e1..1c748544 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -1256,6 +1256,17 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, LCN lcn_seek_from = -1; VCN cur_vcn, from_vcn; + if (na->ni->mft_no == FILE_Bitmap) { + /* + * Filling a hole in the main bitmap implies allocating + * clusters, which is likely to imply updating the + * bitmap in a cluster being allocated. + * Not supported now, could lead to endless recursions. + */ + ntfs_log_error("Corrupt $BitMap not fully allocated\n"); + errno = EIO; + goto err_out; + } to_write = min(count, ((*rl)->length << vol->cluster_size_bits) - *ofs); cur_vcn = (*rl)->vcn; From 2bf50778043dfebd8c1cb45859446069d99c6d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 40/52] Checked consistency of index blocks Improved existing consistency checks of index blocks and grouped them into a specific function. --- include/ntfs-3g/index.h | 2 + libntfs-3g/dir.c | 52 ++--------------- libntfs-3g/index.c | 125 +++++++++++++++++++++++++--------------- 3 files changed, 87 insertions(+), 92 deletions(-) diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index 4e3f73f7..22dcbb80 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -139,6 +139,8 @@ extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, extern void ntfs_index_ctx_put(ntfs_index_context *ictx); extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx); +extern int ntfs_index_block_inconsistent(const INDEX_BLOCK *ib, u32 block_size, + u64 inum, VCN vcn); extern int ntfs_index_entry_consistent(const INDEX_ENTRY *ie, COLLATION_RULES collation_rule, u64 inum); extern int ntfs_index_lookup(const void *key, const int key_len, diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index d4c8204e..142c6826 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -407,33 +407,12 @@ descend_into_child_node: goto close_err_out; } - if (sle64_to_cpu(ia->index_block_vcn) != vcn) { - ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " - "from expected VCN (0x%llx).\n", - (long long)sle64_to_cpu(ia->index_block_vcn), - (long long)vcn); - errno = EIO; - goto close_err_out; - } - if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { - ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx " - "has a size (%u) differing from the directory " - "specified size (%u).\n", (long long)vcn, - (unsigned long long)dir_ni->mft_no, - (unsigned) le32_to_cpu(ia->index.allocated_size) + 0x18, - (unsigned)index_block_size); + if (ntfs_index_block_inconsistent((INDEX_BLOCK*)ia, index_block_size, + ia_na->ni->mft_no, vcn)) { errno = EIO; goto close_err_out; } index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); - if (((s32)le32_to_cpu(ia->index.index_length) < 0) - || (index_end > (u8*)ia + index_block_size)) { - ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " - "0x%llx exceeds maximum size.\n", - (long long)vcn, (unsigned long long)dir_ni->mft_no); - errno = EIO; - goto close_err_out; - } /* The first index entry. */ ie = (INDEX_ENTRY*)((u8*)&ia->index + @@ -1372,33 +1351,12 @@ find_next_index_buffer: } ia_start = ia_pos & ~(s64)(index_block_size - 1); - if (sle64_to_cpu(ia->index_block_vcn) != ia_start >> - index_vcn_size_bits) { - ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " - "from expected VCN (0x%llx) in inode 0x%llx.\n", - (long long)sle64_to_cpu(ia->index_block_vcn), - (long long)ia_start >> index_vcn_size_bits, - (unsigned long long)dir_ni->mft_no); - goto dir_err_out; - } - if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { - ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode %lld " - "has a size (%u) differing from the directory " - "specified size (%u).\n", (long long)ia_start >> - index_vcn_size_bits, - (unsigned long long)dir_ni->mft_no, - (unsigned) le32_to_cpu(ia->index.allocated_size) - + 0x18, (unsigned)index_block_size); + if (ntfs_index_block_inconsistent((INDEX_BLOCK*)ia, index_block_size, + ia_na->ni->mft_no, ia_start >> index_vcn_size_bits)) { goto dir_err_out; } index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); - if (index_end > (u8*)ia + index_block_size) { - ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " - "%lld exceeds maximum size.\n", - (long long)ia_start >> index_vcn_size_bits, - (unsigned long long)dir_ni->mft_no); - goto dir_err_out; - } + /* The first index entry. */ ie = (INDEX_ENTRY*)((u8*)&ia->index + le32_to_cpu(ia->index.entries_offset)); diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index c8c03481..943450e2 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -388,50 +388,6 @@ static INDEX_ENTRY *ntfs_ie_dup_novcn(INDEX_ENTRY *ie) return dup; } -static int ntfs_ia_check(ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn) -{ - u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size) + 0x18; - - ntfs_log_trace("Entering\n"); - - if (!ntfs_is_indx_record(ib->magic)) { - - ntfs_log_error("Corrupt index block signature: vcn %lld inode " - "%llu\n", (long long)vcn, - (unsigned long long)icx->ni->mft_no); - return -1; - } - - if (sle64_to_cpu(ib->index_block_vcn) != vcn) { - - ntfs_log_error("Corrupt index block: VCN (%lld) is different " - "from expected VCN (%lld) in inode %llu\n", - (long long)sle64_to_cpu(ib->index_block_vcn), - (long long)vcn, - (unsigned long long)icx->ni->mft_no); - return -1; - } - - if (ib_size != icx->block_size) { - - ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu " - "has a size (%u) differing from the index " - "specified size (%u)\n", (long long)vcn, - (unsigned long long)icx->ni->mft_no, ib_size, - icx->block_size); - return -1; - } - if (((s32)le32_to_cpu(ib->index.index_length) < 0) - || ((u8*)&ib->index + le32_to_cpu(ib->index.index_length) > - (u8*)ib + icx->block_size)) { - ntfs_log_error("Size of index buffer (%lld) of inode %llu " - "exceeds maximum size.\n", (long long)vcn, - (unsigned long long)icx->ni->mft_no); - return -1; - } - return 0; -} - static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, u32 name_len, ntfs_attr_search_ctx **ctx) { @@ -477,6 +433,82 @@ static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len) return ir; } +/* + * Check the consistency of an index block + * + * Make sure the index block does not overflow from the index record. + * The size of block is assumed to have been checked to be what is + * defined in the index root. + * + * Returns 0 if no error was found + * -1 otherwise (with errno unchanged) + * + * |<--->| offsetof(INDEX_BLOCK, index) + * | |<--->| sizeof(INDEX_HEADER) + * | | | + * | | | seq index entries unused + * |=====|=====|=====|===========================|==============| + * | | | | | + * | |<--------->| entries_offset | | + * | |<---------------- index_length ------->| | + * | |<--------------------- allocated_size --------------->| + * |<--------------------------- block_size ------------------->| + * + * size(INDEX_HEADER) <= ent_offset < ind_length <= alloc_size < bk_size + */ + +int ntfs_index_block_inconsistent(const INDEX_BLOCK *ib, u32 block_size, + u64 inum, VCN vcn) +{ + u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size) + + offsetof(INDEX_BLOCK, index); + + if (!ntfs_is_indx_record(ib->magic)) { + ntfs_log_error("Corrupt index block signature: vcn %lld inode " + "%llu\n", (long long)vcn, + (unsigned long long)inum); + return -1; + } + + if (sle64_to_cpu(ib->index_block_vcn) != vcn) { + ntfs_log_error("Corrupt index block: VCN (%lld) is different " + "from expected VCN (%lld) in inode %llu\n", + (long long)sle64_to_cpu(ib->index_block_vcn), + (long long)vcn, + (unsigned long long)inum); + return -1; + } + + if (ib_size != block_size) { + ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu " + "has a size (%u) differing from the index " + "specified size (%u)\n", (long long)vcn, + (unsigned long long)inum, ib_size, + (unsigned int)block_size); + return -1; + } + if (le32_to_cpu(ib->index.entries_offset) < sizeof(INDEX_HEADER)) { + ntfs_log_error("Invalid index entry offset in inode %lld\n", + (unsigned long long)inum); + return -1; + } + if (le32_to_cpu(ib->index.index_length) + <= le32_to_cpu(ib->index.entries_offset)) { + ntfs_log_error("No space for index entries in inode %lld\n", + (unsigned long long)inum); + return -1; + } + if (le32_to_cpu(ib->index.allocated_size) + < le32_to_cpu(ib->index.index_length)) { + ntfs_log_error("Index entries overflow in inode %lld\n", + (unsigned long long)inum); + return -1; + } + + return (0); +} + + /* * Check the consistency of an index entry * @@ -671,8 +703,11 @@ static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst) return -1; } - if (ntfs_ia_check(icx, dst, vcn)) + if (ntfs_index_block_inconsistent((INDEX_BLOCK*)dst, icx->block_size, + icx->ia_na->ni->mft_no, vcn)) { + errno = EIO; return -1; + } return 0; } From e70d10d8488d3dea835d0bd85e16e567af23cd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 41/52] Added a check of the minimal length of some attributes The minimal lengths of STANDARD_ATTRIBUTE and OBJECT_ID were not checked and could lead to out-of-buffer access. --- libntfs-3g/attrib.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 1c748544..628fa819 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3492,7 +3492,28 @@ int ntfs_attr_consistent(const ATTR_RECORD *a, const MFT_REF mref) } break; case AT_STANDARD_INFORMATION : + if (a->non_resident + || (le32_to_cpu(a->value_length) + < offsetof(STANDARD_INFORMATION, + v1_end))) { + ntfs_log_error("Corrupt standard information" + " in MFT record %lld\n", + (long long)inum); + errno = EIO; + ret = -1; + } + break; case AT_OBJECT_ID : + if (a->non_resident + || (le32_to_cpu(a->value_length) + < sizeof(GUID))) { + ntfs_log_error("Corrupt object id" + " in MFT record %lld\n", + (long long)inum); + errno = EIO; + ret = -1; + } + break; case AT_VOLUME_NAME : case AT_EA_INFORMATION : if (a->non_resident) { From b95b4ba1a54e589475bee4c88d33f020b4d0d562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 42/52] Rejected negative data length in readall() The negative data length of an attribute is an indication of a probable corruption and must be rejected. --- libntfs-3g/attrib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 628fa819..f7a2ef18 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -6930,9 +6930,10 @@ void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, * index bitmaps may need more, but still limited by * the number of clusters. */ - if ((na->data_size > 65536) + if (((u64)na->data_size > 65536) && ((type != AT_BITMAP) - || ((na->data_size << 3) > ni->vol->nr_clusters))) { + || ((u64)na->data_size > + (u64)((ni->vol->nr_clusters + 7) >> 3)))) { ntfs_log_error("Corrupt attribute 0x%lx in inode %lld\n", (long)le32_to_cpu(type),(long long)ni->mft_no); errno = EOVERFLOW; From 0911ef206dc8fea68e18282a1ee5f4489cf0ca1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 43/52] Rejected negative data length in an attribute list The negative data length of an attribute list is an indication of a probable corruption and must be rejected. --- libntfs-3g/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index fe57efaa..8819ff1d 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -231,9 +231,9 @@ static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref) l = ntfs_get_attribute_value_length(ctx->attr); if (!l) goto put_err_out; - if (l > 0x40000) { + if ((u64)l > 0x40000) { errno = EIO; - ntfs_log_perror("Too large attrlist attribute (%lld), inode " + ntfs_log_perror("Too large attrlist attribute (%llu), inode " "%lld", (long long)l, (long long)MREF(mref)); goto put_err_out; } From 4462f82580a691619e98683efb4141ec00119687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 44/52] Reset the resident attribute offset when appending from none When there is no resident attribute value, its offset is unsafe, so better to recompute it when appending data. --- libntfs-3g/attrib.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index f7a2ef18..d2d83ac6 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -4828,6 +4828,13 @@ int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size); + if (!a->value_length) { + /* Offset is unsafe when no value */ + int offset = ((offsetof(ATTR_RECORD, resident_end) + + a->name_length*sizeof(ntfschar) - 1) | 7) + 1; + a->value_offset = cpu_to_le16(offset); + } + /* Resize the resident part of the attribute record. */ if ((ret = ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) + new_size + 7) & ~7)) < 0) From 81725f6a54863145e6e715d0c978ae6edaeb9ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 45/52] Made sure the requested compression block size is supported Compressed files can only be opened if NTFS version < 3.0, and the only supported compression block size is 16 clusters long. --- libntfs-3g/attrib.c | 22 ++++++++++++++++++++++ libntfs-3g/compress.c | 18 ++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index d2d83ac6..f4f31cf6 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -489,6 +489,17 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, } if (a->non_resident) { + if (((a->flags & ATTR_COMPRESSION_MASK) + || a->compression_unit) + && (ni->vol->major_ver < 3)) { + errno = EIO; + ntfs_log_perror("Compressed inode %lld not allowed" + " on NTFS %d.%d", + (unsigned long long)ni->mft_no, + ni->vol->major_ver, + ni->vol->major_ver); + goto put_err_out; + } if ((a->flags & ATTR_COMPRESSION_MASK) && !a->compression_unit) { errno = EIO; @@ -497,6 +508,17 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, (unsigned long long)ni->mft_no, le32_to_cpu(type)); goto put_err_out; } + if ((a->flags & ATTR_COMPRESSION_MASK) + && (a->compression_unit + != STANDARD_COMPRESSION_UNIT)) { + errno = EIO; + ntfs_log_perror("Compressed inode %lld attr 0x%lx has " + "an unsupported compression unit %d", + (unsigned long long)ni->mft_no, + (long)le32_to_cpu(type), + (int)a->compression_unit); + goto put_err_out; + } ntfs_attr_init(na, TRUE, a->flags, a->flags & ATTR_IS_ENCRYPTED, a->flags & ATTR_IS_SPARSE, diff --git a/libntfs-3g/compress.c b/libntfs-3g/compress.c index 0c22bc9c..390b2d97 100644 --- a/libntfs-3g/compress.c +++ b/libntfs-3g/compress.c @@ -752,6 +752,12 @@ s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b) /* If it is a resident attribute, simply use ntfs_attr_pread(). */ if (!NAttrNonResident(na)) return ntfs_attr_pread(na, pos, count, b); + if (na->compression_block_size < NTFS_SB_SIZE) { + ntfs_log_error("Unsupported compression block size %ld\n", + (long)na->compression_block_size); + errno = EOVERFLOW; + return (-1); + } total = total2 = 0; /* Zero out reads beyond initialized size. */ if (pos + count > na->initialized_size) { @@ -1702,6 +1708,12 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, errno = EIO; return (-1); } + if (na->compression_block_size < NTFS_SB_SIZE) { + ntfs_log_error("Unsupported compression block size %ld\n", + (long)na->compression_block_size); + errno = EOVERFLOW; + return (-1); + } if (wrl->vcn < *update_from) *update_from = wrl->vcn; written = -1; /* default return */ @@ -1883,6 +1895,12 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs, errno = EIO; return (-1); } + if (na->compression_block_size < NTFS_SB_SIZE) { + ntfs_log_error("Unsupported compression block size %ld\n", + (long)na->compression_block_size); + errno = EOVERFLOW; + return (-1); + } if (wrl->vcn < *update_from) *update_from = wrl->vcn; vol = na->ni->vol; From bb4456d3397c5eac559c35b2310d0bb02cd15525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 46/52] Redesigned the INDEX_ROOT consistency checks By ordering the values from smallest to biggest, there is less chance to be caught by an arithmetic overflow. --- libntfs-3g/attrib.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index f4f31cf6..b831c2bb 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3496,16 +3496,15 @@ int ntfs_attr_consistent(const ATTR_RECORD *a, const MFT_REF mref) if (a->non_resident || (le32_to_cpu(a->value_length) < offsetof(INDEX_ROOT, index.reserved)) + || (le32_to_cpu(ir->index.entries_offset) + < sizeof(INDEX_HEADER)) || (le32_to_cpu(ir->index.index_length) - & 0xff000000) - || ((le32_to_cpu(a->value_length) - - le32_to_cpu(ir->index.index_length)) - < offsetof(INDEX_ROOT,index)) - || ((le32_to_cpu(a->value_length) - - le32_to_cpu(ir->index.index_length)) < le32_to_cpu(ir->index.entries_offset)) - || (le32_to_cpu(ir->index.index_length) - > le32_to_cpu(ir->index.allocated_size))) { + || (le32_to_cpu(ir->index.allocated_size) + < le32_to_cpu(ir->index.index_length)) + || (le32_to_cpu(a->value_length) + < (le32_to_cpu(ir->index.allocated_size) + + offsetof(INDEX_ROOT, reserved)))) { ntfs_log_error("Corrupt index root" " in MFT record %lld.\n", (long long)inum); From 45141516d7f9512e1a340ff466b35a9d66da4df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 47/52] Renamed ntfs_attr_consistent() as ntfs_attr_inconsistent() The original name was error prone while checking the condition. --- include/ntfs-3g/attrib.h | 2 +- libntfs-3g/attrib.c | 2 +- libntfs-3g/mft.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h index e2cf41b3..f2a180c9 100644 --- a/include/ntfs-3g/attrib.h +++ b/include/ntfs-3g/attrib.h @@ -398,7 +398,7 @@ extern int ntfs_attr_data_write(ntfs_inode *ni, const char *buf, size_t size, off_t offset); extern int ntfs_attr_shrink_size(ntfs_inode *ni, ntfschar *stream_name, int stream_name_len, off_t offset); -extern int ntfs_attr_consistent(const ATTR_RECORD *a, const MFT_REF mref); +extern int ntfs_attr_inconsistent(const ATTR_RECORD *a, const MFT_REF mref); #endif /* defined _NTFS_ATTRIB_H */ diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index b831c2bb..420fb493 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3402,7 +3402,7 @@ not_found: * -1 with errno = EIO otherwise */ -int ntfs_attr_consistent(const ATTR_RECORD *a, const MFT_REF mref) +int ntfs_attr_inconsistent(const ATTR_RECORD *a, const MFT_REF mref) { const FILE_NAME_ATTR *fn; const INDEX_ROOT *ir; diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index cc244871..d0a601ff 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -286,7 +286,7 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, && (le32_to_cpu(a->type) >= le32_to_cpu(previous_type))) { if ((le32_to_cpu(a->length) <= (u32)space) && !(le32_to_cpu(a->length) & 7)) { - if (!ntfs_attr_consistent(a, mref)) { + if (!ntfs_attr_inconsistent(a, mref)) { previous_type = a->type; offset += le32_to_cpu(a->length); space -= le32_to_cpu(a->length); From a337c4c1eb75638026bfe4b385a3335801f5b001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 19 Jul 2021 09:23:23 +0200 Subject: [PATCH 48/52] Renamed ntfs_index_entry_consistent() as ntfs_index_entry_inconsistent() The original name was error prone when checking the condition. --- include/ntfs-3g/index.h | 2 +- libntfs-3g/dir.c | 8 ++++---- libntfs-3g/index.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index 22dcbb80..d001863a 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -141,7 +141,7 @@ extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx); extern int ntfs_index_block_inconsistent(const INDEX_BLOCK *ib, u32 block_size, u64 inum, VCN vcn); -extern int ntfs_index_entry_consistent(const INDEX_ENTRY *ie, +extern int ntfs_index_entry_inconsistent(const INDEX_ENTRY *ie, COLLATION_RULES collation_rule, u64 inum); extern int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ictx) __attribute_warn_unused_result__; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 142c6826..5cab5c54 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -321,7 +321,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, break; /* The file name must not overflow from the entry */ - if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME, dir_ni->mft_no)) { errno = EIO; goto put_err_out; @@ -442,7 +442,7 @@ descend_into_child_node: break; /* The file name must not overflow from the entry */ - if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME, dir_ni->mft_no)) { errno = EIO; goto close_err_out; @@ -1247,7 +1247,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, continue; /* The file name must not overflow from the entry */ - if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME, dir_ni->mft_no)) { errno = EIO; goto dir_err_out; @@ -1389,7 +1389,7 @@ find_next_index_buffer: continue; /* The file name must not overflow from the entry */ - if (ntfs_index_entry_consistent(ie, COLLATION_FILE_NAME, + if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME, dir_ni->mft_no)) { errno = EIO; goto dir_err_out; diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 943450e2..e48d6aaf 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -522,7 +522,7 @@ int ntfs_index_block_inconsistent(const INDEX_BLOCK *ib, u32 block_size, * -1 otherwise (with errno unchanged) */ -int ntfs_index_entry_consistent(const INDEX_ENTRY *ie, +int ntfs_index_entry_inconsistent(const INDEX_ENTRY *ie, COLLATION_RULES collation_rule, u64 inum) { int ret; @@ -614,7 +614,7 @@ static int ntfs_ie_lookup(const void *key, const int key_len, return STATUS_ERROR; } /* Make sure key and data do not overflow from entry */ - if (ntfs_index_entry_consistent(ie, icx->ir->collation_rule, + if (ntfs_index_entry_inconsistent(ie, icx->ir->collation_rule, icx->ni->mft_no)) { errno = EIO; return STATUS_ERROR; From 1261e6b60a33a5984c5662313505998625318d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 26 Jul 2021 08:49:45 +0200 Subject: [PATCH 49/52] Fixed the detection of the end of attribute list The recent detection of a truncated attribute list entry overlooked the normal detection of the end of list. Moreover the check for name overflow is to be done later and not needed at this stage. --- libntfs-3g/attrib.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 420fb493..e8d6fafb 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -3092,10 +3092,11 @@ static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, 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)) + if ((u8*)al_entry == al_end) + goto not_found; + /* Preliminary check for small entry */ + if ((p2n(al_end) - p2n(al_entry)) + < (long)offsetof(ATTR_LIST_ENTRY, name)) goto corrupt; /* * If this is an enumeration and the attribute list attribute From 21b49600eac039034723588e2a7c1e4af3bb53a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sat, 14 Aug 2021 08:44:08 +0200 Subject: [PATCH 50/52] Configured for version 2021.8.14 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index e2005cc5..081fa432 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2021.5.19],[ntfs-3g-devel@lists.sf.net]) +AC_INIT([ntfs-3g],[2021.8.14],[ntfs-3g-devel@lists.sf.net]) LIBNTFS_3G_VERSION="89" AC_CONFIG_SRCDIR([src/ntfs-3g.c]) From a213e6352bb03f18db2dac115f310dfffbb7c224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 22 Aug 2021 17:01:39 +0200 Subject: [PATCH 51/52] Defined Github as the host for documentation Documentation and support is now on github.com/tuxera --- ChangeLog | 7 ++----- NEWS | 6 ++---- README | 42 +++++++++++++++++------------------------- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/ChangeLog b/ChangeLog index a49d76a1..68c1565e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,3 @@ +ChangeLog can be found at : -Detailed ChangeLog can be found at - http://www.tuxera.com/community/release-history/ - -The changes and history may also be found on the source repository : - http://sourceforge.net/p/ntfs-3g/ntfs-3g/ci/edge/tree/ + https://github.com/tuxera/ntfs-3g/wiki diff --git a/NEWS b/NEWS index 3a7effde..1040fd8b 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,3 @@ +Project information can be found at : -Project news are at http://tuxera.com/community/ntfs-3g-download/ - -Release notes are maintained at http://tuxera.com/community/release-history/ - + https://github.com/tuxera/ntfs-3g/ diff --git a/README b/README index f072c0ea..9962af40 100644 --- a/README +++ b/README @@ -3,10 +3,10 @@ INTRODUCTION ============ The NTFS-3G driver is an open source, freely available read/write NTFS driver -for Linux, FreeBSD, Mac OS X, NetBSD, OpenSolaris, QNX and Haiku. It provides +for Linux, FreeBSD, macOS, NetBSD, OpenIndiana, QNX and Haiku. It provides safe and fast handling of the Windows XP, Windows Server 2003, Windows 2000, -Windows Vista, Windows Server 2008, Windows 7, Windows 8 and Windows 10 file -systems. +Windows Vista, Windows Server 2008, Windows 7, Windows 8, Windows Server 2012, +Windows Server 2016, Windows 10 and Windows Server 2019 NTFS file systems. The purpose of the project is to develop, quality assurance and support a trustable, featureful and high performance solution for hardware platforms @@ -19,25 +19,22 @@ Besides the common file system features, NTFS-3G has support for file ownership and permissions, POSIX ACLs, junction points, extended attributes and creating internally compressed files (parameter files in the directory .NTFS-3G may be required to enable them). The new compressed file formats -available in Windows 10 can also be read through a plugin. For using -advanced features, please get the instructions from - - https://www.tuxera.com/community/ntfs-3g-advanced/ +available in Windows 10 can also be read through a plugin. News, support answers, problem submission instructions, support and discussion -forums, performance numbers and other information are available on the project -web site at - - https://www.tuxera.com/community/ - -The project's official git repository is now hosted on GitHub: +forums, and other information are available on the project web site at https://github.com/tuxera/ntfs-3g +The project has been funded, supported and maintained since 2008 by Tuxera: + + https://tuxera.com + + LICENSES ======== -All the NTFS related components : the file system drivers, the ntfsprogs +All the NTFS related components: the file system drivers, the ntfsprogs utilities and the shared library libntfs-3g are distributed under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later @@ -46,6 +43,7 @@ version. See the included file COPYING. The fuse-lite library is distributed under the terms of the GNU LGPLv2. See the included file COPYING.LIB. + QUICK INSTALLATION ================== @@ -78,12 +76,6 @@ There are also a few make targets for building parts : make drivers : only build drivers and libraries, without ntfsprogs make ntfsprogs : only build ntfsprogs and libntfs-3g, without drivers -Non-Linux: Please see - - http://www.tuxera.com/community/ntfs-3g-download/ - -for known OS specific installation and source packages, but generally -the same procedures apply. USAGE ===== @@ -111,23 +103,23 @@ TESTING WITHOUT INSTALLING Newer versions of ntfs-3g can be tested without installing anything and without disturbing an existing installation. Just configure and make as shown previously. This will create the scripts ntfs-3g and lowntfs-3g -in the src directory, which you may activate for testing : +in the src directory, which you may activate for testing: ./configure make -then, as root : +then, as root: src/ntfs-3g [-o mount-options] /dev/sda1 /mnt/windows -And, to end the test, unmount the usual way : +And, to end the test, unmount the usual way: umount /dev/sda1 NTFS UTILITIES ============== -The ntfsprogs includes utilities for doing all required tasks to NTFS -partitions. In general, just run a utility without any command line +The ntfsprogs directory includes utilities for doing all required tasks to +NTFS partitions. In general, just run a utility without any command line options to display the version number and usage syntax. The following utilities are so far implemented: From 31ac7e4f0fc07beeca455eba6a8f02abd7e4d76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 22 Aug 2021 17:05:00 +0200 Subject: [PATCH 52/52] Configured for version 2021.8.22 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 081fa432..029b334f 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ # Autoconf AC_PREREQ(2.59) -AC_INIT([ntfs-3g],[2021.8.14],[ntfs-3g-devel@lists.sf.net]) +AC_INIT([ntfs-3g],[2021.8.22],[ntfs-3g-devel@lists.sf.net]) LIBNTFS_3G_VERSION="89" AC_CONFIG_SRCDIR([src/ntfs-3g.c])