Commit a5d1079c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ntfs3 updates from Konstantin Komarov:
 "New:
   - reject inodes with zero non-DOS link count
   - return folios from ntfs_lock_new_page()
   - subset of W=1 warnings for stricter checks
   - work around -Wmaybe-uninitialized warnings
   - buffer boundary checks to run_unpack()
   - terminate the cached volume label after UTF-8 conversion

  Fixes:
   - check return value of indx_find to avoid infinite loop
   - prevent uninitialized lcn caused by zero len
   - increase CLIENT_REC name field size to prevent buffer overflow
   - missing run load for vcn0 in attr_data_get_block_locked()
   - memory leak in indx_create_allocate()
   - OOB write in attr_wof_frame_info()
   - mount failure on volumes with fragmented MFT bitmap
   - integer overflow in run_unpack() volume boundary check
   - validate rec->used in journal-replay file record check

  Updates:
   - resolve compare function in public index APIs
   - $LXDEV xattr lookup
   - potential double iput on d_make_root() failure
   - initialize err in ni_allocate_da_blocks_locked()
   - correct the pre_alloc condition in attr_allocate_clusters()"

* tag 'ntfs3_for_7.1' of https://github.com/Paragon-Software-Group/linux-ntfs3:
  fs/ntfs3: fix Smatch warnings
  fs/ntfs3: validate rec->used in journal-replay file record check
  fs/ntfs3: terminate the cached volume label after UTF-8 conversion
  fs/ntfs3: fix potential double iput on d_make_root() failure
  ntfs3: fix integer overflow in run_unpack() volume boundary check
  ntfs3: add buffer boundary checks to run_unpack()
  ntfs3: fix mount failure on volumes with fragmented MFT bitmap
  fs/ntfs3: fix $LXDEV xattr lookup
  ntfs3: fix OOB write in attr_wof_frame_info()
  ntfs3: fix memory leak in indx_create_allocate()
  ntfs3: work around false-postive -Wmaybe-uninitialized warnings
  fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()
  fs/ntfs3: increase CLIENT_REC name field size
  fs/ntfs3: prevent uninitialized lcn caused by zero len
  fs/ntfs3: add a subset of W=1 warnings for stricter checks
  fs/ntfs3: return folios from ntfs_lock_new_page()
  fs/ntfs3: resolve compare function in public index APIs
  ntfs3: reject inodes with zero non-DOS link count
parents a5e1c3b6 819bd270
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -3,6 +3,26 @@
# Makefile for the ntfs3 filesystem support.
#

# Subset of W=1 warnings
subdir-ccflags-y += -Wextra -Wunused -Wno-unused-parameter
subdir-ccflags-y += -Wmissing-declarations
subdir-ccflags-y += -Wmissing-format-attribute
subdir-ccflags-y += -Wmissing-prototypes
subdir-ccflags-y += -Wold-style-definition
subdir-ccflags-y += -Wmissing-include-dirs
condflags := \
	$(call cc-option, -Wunused-but-set-variable)		\
	$(call cc-option, -Wunused-const-variable)		\
	$(call cc-option, -Wpacked-not-aligned)			\
	$(call cc-option, -Wstringop-truncation)		\
	$(call cc-option, -Wmaybe-uninitialized)
subdir-ccflags-y += $(condflags)
# The following turn off the warnings enabled by -Wextra
subdir-ccflags-y += -Wno-missing-field-initializers
subdir-ccflags-y += -Wno-sign-compare
subdir-ccflags-y += -Wno-type-limits
subdir-ccflags-y += -Wno-shift-negative-value

# to check robot warnings
ccflags-y += -Wint-to-pointer-cast \
	$(call cc-option,-Wunused-but-set-variable,-Wunused-const-variable) \
+22 −1
Original line number Diff line number Diff line
@@ -173,7 +173,7 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,

		if (err == -ENOSPC && pre) {
			pre = 0;
			if (*pre_alloc)
			if (pre_alloc)
				*pre_alloc = 0;
			continue;
		}
@@ -1152,6 +1152,21 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen,
			if (err)
				goto out;
		}

		if (vcn0 < svcn || evcn1 <= vcn0) {
			struct ATTRIB *attr2;

			attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL,
					       0, &vcn0, &mi);
			if (!attr2) {
				err = -EINVAL;
				goto out;
			}
			err = attr_load_runs(attr2, ni, run, NULL);
			if (err)
				goto out;
		}

		da = false; /* no delalloc for compressed file. */
	}

@@ -1576,6 +1591,12 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
			u64 from = vbo[i] & ~(u64)(PAGE_SIZE - 1);
			u64 to = min(from + PAGE_SIZE, wof_size);

			if (from >= wof_size) {
				_ntfs_bad_inode(&ni->vfs_inode);
				err = -EINVAL;
				goto out1;
			}

			err = attr_load_runs_range(ni, ATTR_DATA, WOF_NAME,
						   ARRAY_SIZE(WOF_NAME), run,
						   from, to);
+27 −22
Original line number Diff line number Diff line
@@ -1852,27 +1852,31 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
	return REPARSE_LINK;
}

static struct page *ntfs_lock_new_page(struct address_space *mapping,
static struct folio *ntfs_lock_new_page(struct address_space *mapping,
					pgoff_t index, gfp_t gfp)
{
	struct folio *folio = __filemap_get_folio(
		mapping, index, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
	struct page *page;
	struct folio *folio = __filemap_get_folio(mapping, index,
			FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);

	if (IS_ERR(folio))
		return ERR_CAST(folio);
		return folio;

	if (!folio_test_uptodate(folio))
		return folio_file_page(folio, index);
	if (!folio_test_uptodate(folio)) {
		struct page *page = folio_file_page(folio, index);

		if (IS_ERR(page))
			return ERR_CAST(page);
		return page_folio(page);
	}

	/* Use a temporary page to avoid data corruption */
	folio_unlock(folio);
	folio_put(folio);
	page = alloc_page(gfp);
	if (!page)
	folio = folio_alloc(gfp, 0);
	if (!folio)
		return ERR_PTR(-ENOMEM);
	__SetPageLocked(page);
	return page;
	__folio_set_locked(folio);
	return folio;
}

/*
@@ -1894,6 +1898,7 @@ int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio)
	u32 i, idx, frame_size, pages_per_frame;
	gfp_t gfp_mask;
	struct page *pg;
	struct folio *f;

	if (vbo >= i_size_read(&ni->vfs_inode)) {
		folio_zero_range(folio, 0, folio_size(folio));
@@ -1929,12 +1934,12 @@ int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio)
		if (i == idx)
			continue;

		pg = ntfs_lock_new_page(mapping, index, gfp_mask);
		if (IS_ERR(pg)) {
			err = PTR_ERR(pg);
		f = ntfs_lock_new_page(mapping, index, gfp_mask);
		if (IS_ERR(f)) {
			err = PTR_ERR(f);
			goto out1;
		}
		pages[i] = pg;
		pages[i] = &f->page;
	}

	ni_lock(ni);
@@ -2023,18 +2028,18 @@ int ni_decompress_file(struct ntfs_inode *ni)
		}

		for (i = 0; i < pages_per_frame; i++, index++) {
			struct page *pg;
			struct folio *f;

			pg = ntfs_lock_new_page(mapping, index, gfp_mask);
			if (IS_ERR(pg)) {
			f = ntfs_lock_new_page(mapping, index, gfp_mask);
			if (IS_ERR(f)) {
				while (i--) {
					unlock_page(pages[i]);
					put_page(pages[i]);
				}
				err = PTR_ERR(pg);
				err = PTR_ERR(f);
				goto out;
			}
			pages[i] = pg;
			pages[i] = &f->page;
		}

		err = ni_read_frame(ni, vbo, pages, pages_per_frame, 1);
@@ -3262,7 +3267,7 @@ int ni_allocate_da_blocks(struct ntfs_inode *ni)
 */
int ni_allocate_da_blocks_locked(struct ntfs_inode *ni)
{
	int err;
	int err = 0;

	if (!ni->file.run_da.count)
		return 0;
+13 −3
Original line number Diff line number Diff line
@@ -45,10 +45,10 @@ struct CLIENT_REC {
	__le16 seq_num;     // 0x14:
	u8 align[6];        // 0x16:
	__le32 name_bytes;  // 0x1C: In bytes.
	__le16 name[32];    // 0x20: Name of client.
	__le16 name[64];    // 0x20: Name of client.
};

static_assert(sizeof(struct CLIENT_REC) == 0x60);
static_assert(sizeof(struct CLIENT_REC) == 0xa0);

/* Two copies of these will exist at the beginning of the log file */
struct RESTART_AREA {
@@ -2791,13 +2791,14 @@ static inline bool check_file_record(const struct MFT_REC *rec,
	u16 fn = le16_to_cpu(rec->rhdr.fix_num);
	u16 ao = le16_to_cpu(rec->attr_off);
	u32 rs = sbi->record_size;
	u32 used = le32_to_cpu(rec->used);

	/* Check the file record header for consistency. */
	if (rec->rhdr.sign != NTFS_FILE_SIGNATURE ||
	    fo > (SECTOR_SIZE - ((rs >> SECTOR_SHIFT) + 1) * sizeof(short)) ||
	    (fn - 1) * SECTOR_SIZE != rs || ao < MFTRECORD_FIXUP_OFFSET_1 ||
	    ao > sbi->record_size - SIZEOF_RESIDENT || !is_rec_inuse(rec) ||
	    le32_to_cpu(rec->total) != rs) {
	    le32_to_cpu(rec->total) != rs || used > rs || used < ao) {
		return false;
	}

@@ -2809,6 +2810,15 @@ static inline bool check_file_record(const struct MFT_REC *rec,
		return false;
	}

	/*
	 * The do_action() handlers compute memmove lengths as
	 * "rec->used - <offset of validated attr>", which underflows when
	 * rec->used is smaller than the attribute walk reached.  At this
	 * point attr is the ATTR_END marker; rec->used must cover it.
	 */
	if (used < PtrOffset(rec, attr) + sizeof(attr->type))
		return false;

	return true;
}

+2 −2
Original line number Diff line number Diff line
@@ -1440,8 +1440,8 @@ int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr,
	u16 fo = le16_to_cpu(rhdr->fix_off);
	u16 fn = le16_to_cpu(rhdr->fix_num);
	u32 idx;
	__le16 *fixup;
	__le16 sample;
	__le16 *fixup = NULL;
	__le16 sample = cpu_to_le16(-1u);

	if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- ||
	    fn * SECTOR_SIZE > bytes) {
Loading