Unverified Commit 45205929 authored by Christian Brauner's avatar Christian Brauner
Browse files

Merge patch series "netfs: Miscellaneous fixes"

David Howells <dhowells@redhat.com> says:

Here are the outstanding miscellaneous fixes for netfslib gathered together
and with some fixes-to-fixes folded down and one rearrangement.  Various
Sashiko review comments[1][2][3][4][5] are addressed:

 (1) Fix subrequest cancellation cleanup in DIO read and single-read.

 (2) Fix missing locking around retry adding new subrequests.

 (3) Fix read and write result collection to use barriering correctly to
     access a request's subrequest lists without taking a lock.

     This adds list_add_tail_release() and
     list_first_entry_or_null_acquire() to appropriate incorporate
     barriering into some list functions.

 (4) Fix netfs_read_to_pagecache() to pause on subrequest I/O failure.

 (5) Fix the potential for 64-bit tearing on a 32-bit machine when reading
     netfs_inode->remote_i_size and ->zero_point by using much the same
     mechanism as is used for ->i_size.

 (6) Fix the calculation of zero_point in netfs_release_folio() to limit it
     to ->remote_i_size, not ->i_size.

 (7) Fix triggering of a VM_BUG_ON_FOLIO() in netfs_write_begin().

 (8) Fix a potentially uninitialised error value in
     netfs_extract_user_iter().

 (9) Fix error handling in netfs_extract_user_iter().

(10) Fix overrun checking in netfs_extract_user_iter().

(11) Fix netfs_invalidate_folio() to clear the folio dirty bit if all dirty
     data removed.

(12) Defer the emission of trace_netfs_folio() in netfs_perform_write().
     This allows the next patch to emit the correct traces.

(13) Fix the handling of a partially failed copy (ie. EFAULT) into a
     streaming write folio.  Also remove the netfs_folio if a streaming
     write folio is entirely overwritten.

(14) Fix a potential deadlock in writethrough writing.

(15) Fix netfs_read_gaps() to remove the netfs_folio from a filled folio.

(16) Fix netfs_perform_write() to not disable streaming writes when writing
     to an fd that's open O_RDWR.

(17) Fix an early put of the sink page used in netfs_read_gaps(), before
     the request has completed.

(18) Fix request leak in netfs_write_begin() error handling.

(19) Fix a potential UAF in netfs_unlock_abandoned_read_pages() due to
     trying to check index of each folio we're abandoning to see if that
     folio is actually owned by the caller (in which case, we're not
     actually allowed to dereference it).

(20) Fix incorrect adjustment of dirty region when partially invalidating a
     streaming write folio.

(21) Fix the handling of folio->private in netfs_perform_write() and the
     attached netfs_folio and/or group when a streaming write folio is
     modified.

(22) Fix netfs_read_folio() to wait on writeback first (it holds the folio
     lock) otherwise we aren't allowed to look at the netfs_folio struct as
     that could be modified at any time by the writeback collector.

(23) Fix write skipping in dir/symlink writepages.

(24) Fix the locking used by afs_get_link().

[1] https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com
[2] https://sashiko.dev/#/patchset/20260326104544.509518-1-dhowells%40redhat.com
[3] https://sashiko.dev/#/patchset/20260425125426.3855807-1-dhowells%40redhat.com
[4] https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40redhat.com
[5] https://sashiko.dev/#/patchset/20260428131756.922303-1-dhowells%40redhat.com

* patches from https://patch.msgid.link/20260512123404.719402-1-dhowells@redhat.com: (24 commits)
  afs: Fix the locking used by afs_get_link()
  netfs, afs: Fix write skipping in dir/link writepages
  netfs: Fix netfs_read_folio() to wait on writeback
  netfs: Fix folio->private handling in netfs_perform_write()
  netfs: Fix partial invalidation of streaming-write folio
  netfs: Fix potential UAF in netfs_unlock_abandoned_read_pages()
  netfs: Fix leak of request in netfs_write_begin() error handling
  netfs: Fix early put of sink folio in netfs_read_gaps()
  netfs: Fix write streaming disablement if fd open O_RDWR
  netfs: Fix read-gaps to remove netfs_folio from filled folio
  netfs: Fix potential deadlock in write-through mode
  netfs: Fix streaming write being overwritten
  netfs: Defer the emission of trace_netfs_folio()
  netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes gone
  netfs: Fix overrun check in netfs_extract_user_iter()
  netfs: fix error handling in netfs_extract_user_iter()
  netfs: Fix potential uninitialised var in netfs_extract_user_iter()
  netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call
  netfs: Fix zeropoint update where i_size > remote_i_size
  netfs: Fix potential for tearing in ->remote_i_size and ->zero_point
  ...

Link: https://patch.msgid.link/20260512123404.719402-1-dhowells@redhat.com


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parents 859c199b c0410adf
Loading
Loading
Loading
Loading
+0 −13
Original line number Diff line number Diff line
@@ -75,17 +75,4 @@ static inline void v9fs_invalidate_inode_attr(struct inode *inode)

int v9fs_open_to_dotl_flags(int flags);

static inline void v9fs_i_size_write(struct inode *inode, loff_t i_size)
{
	/*
	 * 32-bit need the lock, concurrent updates could break the
	 * sequences and make i_size_read() loop forever.
	 * 64-bit updates are atomic and can skip the locking.
	 */
	if (sizeof(i_size) > sizeof(long))
		spin_lock(&inode->i_lock);
	i_size_write(inode, i_size);
	if (sizeof(i_size) > sizeof(long))
		spin_unlock(&inode->i_lock);
}
#endif
+4 −2
Original line number Diff line number Diff line
@@ -1141,11 +1141,13 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
	mode |= inode->i_mode & ~S_IALLUGO;
	inode->i_mode = mode;

	v9inode->netfs.remote_i_size = stat->length;
	spin_lock(&inode->i_lock);
	netfs_write_remote_i_size(inode, stat->length);
	if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE))
		v9fs_i_size_write(inode, stat->length);
		i_size_write(inode, stat->length);
	/* not real number of blocks, but 512 byte ones ... */
	inode->i_blocks = (stat->length + 512 - 1) >> 9;
	spin_unlock(&inode->i_lock);
	v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR;
}

+8 −4
Original line number Diff line number Diff line
@@ -634,10 +634,12 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
		mode |= inode->i_mode & ~S_IALLUGO;
		inode->i_mode = mode;

		v9inode->netfs.remote_i_size = stat->st_size;
		spin_lock(&inode->i_lock);
		netfs_write_remote_i_size(inode, stat->st_size);
		if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE))
			v9fs_i_size_write(inode, stat->st_size);
			i_size_write(inode, stat->st_size);
		inode->i_blocks = stat->st_blocks;
		spin_unlock(&inode->i_lock);
	} else {
		if (stat->st_result_mask & P9_STATS_ATIME) {
			inode_set_atime(inode, stat->st_atime_sec,
@@ -662,13 +664,15 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
			mode |= inode->i_mode & ~S_IALLUGO;
			inode->i_mode = mode;
		}
		spin_lock(&inode->i_lock);
		if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE) &&
		    stat->st_result_mask & P9_STATS_SIZE) {
			v9inode->netfs.remote_i_size = stat->st_size;
			v9fs_i_size_write(inode, stat->st_size);
			netfs_write_remote_i_size(inode, stat->st_size);
			i_size_write(inode, stat->st_size);
		}
		if (stat->st_result_mask & P9_STATS_BLOCKS)
			inode->i_blocks = stat->st_blocks;
		spin_unlock(&inode->i_lock);
	}
	if (stat->st_result_mask & P9_STATS_GEN)
		inode->i_generation = stat->st_gen;
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ kafs-y := \
	server.o \
	server_list.o \
	super.o \
	symlink.o \
	validation.o \
	vlclient.o \
	vl_alias.o \
+44 −35
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
		      struct dentry *old_dentry, struct inode *new_dir,
		      struct dentry *new_dentry, unsigned int flags);
static int afs_dir_writepages(struct address_space *mapping,
			      struct writeback_control *wbc);

const struct file_operations afs_dir_file_operations = {
	.open		= afs_dir_open,
@@ -68,7 +70,7 @@ const struct inode_operations afs_dir_inode_operations = {
};

const struct address_space_operations afs_dir_aops = {
	.writepages	= afs_single_writepages,
	.writepages	= afs_dir_writepages,
};

const struct dentry_operations afs_fs_dentry_operations = {
@@ -233,23 +235,14 @@ static ssize_t afs_do_read_single(struct afs_vnode *dvnode, struct file *file)
	struct iov_iter iter;
	ssize_t ret;
	loff_t i_size;
	bool is_dir = (S_ISDIR(dvnode->netfs.inode.i_mode) &&
		       !test_bit(AFS_VNODE_MOUNTPOINT, &dvnode->flags));

	i_size = i_size_read(&dvnode->netfs.inode);
	if (is_dir) {
	if (i_size < AFS_DIR_BLOCK_SIZE)
		return afs_bad(dvnode, afs_file_error_dir_small);
	if (i_size > AFS_DIR_BLOCK_SIZE * 1024) {
		trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big);
		return -EFBIG;
	}
	} else {
		if (i_size > AFSPATHMAX) {
			trace_afs_file_error(dvnode, -EFBIG, afs_file_error_dir_big);
			return -EFBIG;
		}
	}

	/* Expand the storage.  TODO: Shrink the storage too. */
	if (dvnode->directory_size < i_size) {
@@ -277,24 +270,18 @@ static ssize_t afs_do_read_single(struct afs_vnode *dvnode, struct file *file)
			 * buffer.
			 */
			ret = -ESTALE;
		} else if (is_dir) {
		} else {
			int ret2 = afs_dir_check(dvnode);

			if (ret2 < 0)
				ret = ret2;
		} else if (i_size < folioq_folio_size(dvnode->directory, 0)) {
			/* NUL-terminate a symlink. */
			char *symlink = kmap_local_folio(folioq_folio(dvnode->directory, 0), 0);

			symlink[i_size] = 0;
			kunmap_local(symlink);
		}
	}

	return ret;
}

ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file)
static ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file)
{
	ssize_t ret;

@@ -1763,13 +1750,20 @@ static int afs_link(struct dentry *from, struct inode *dir,
	return ret;
}

static void afs_symlink_put(struct afs_operation *op)
{
	kfree(op->create.symlink);
	op->create.symlink = NULL;
	afs_create_put(op);
}

static const struct afs_operation_ops afs_symlink_operation = {
	.issue_afs_rpc	= afs_fs_symlink,
	.issue_yfs_rpc	= yfs_fs_symlink,
	.success	= afs_create_success,
	.aborted	= afs_check_for_remote_deletion,
	.edit_dir	= afs_create_edit_dir,
	.put		= afs_create_put,
	.put		= afs_symlink_put,
};

/*
@@ -1779,7 +1773,9 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
		       struct dentry *dentry, const char *content)
{
	struct afs_operation *op;
	struct afs_symlink *symlink;
	struct afs_vnode *dvnode = AFS_FS_I(dir);
	size_t clen = strlen(content);
	int ret;

	_enter("{%llx:%llu},{%pd},%s",
@@ -1791,12 +1787,20 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
		goto error;

	ret = -EINVAL;
	if (strlen(content) >= AFSPATHMAX)
	if (clen >= AFSPATHMAX)
		goto error;

	ret = -ENOMEM;
	symlink = kmalloc_flex(struct afs_symlink, content, clen + 1, GFP_KERNEL);
	if (!symlink)
		goto error;
	refcount_set(&symlink->ref, 1);
	memcpy(symlink->content, content, clen + 1);

	op = afs_alloc_operation(NULL, dvnode->volume);
	if (IS_ERR(op)) {
		ret = PTR_ERR(op);
		kfree(symlink);
		goto error;
	}

@@ -1808,7 +1812,7 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
	op->dentry		= dentry;
	op->ops			= &afs_symlink_operation;
	op->create.reason	= afs_edit_dir_for_symlink;
	op->create.symlink	= content;
	op->create.symlink	= symlink;
	op->mtime		= current_time(dir);
	ret = afs_do_sync_operation(op);
	afs_dir_unuse_cookie(dvnode, ret);
@@ -2192,28 +2196,33 @@ static int afs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
}

/*
 * Write the file contents to the cache as a single blob.
 * Write the directory contents to the cache as a single blob.
 */
int afs_single_writepages(struct address_space *mapping,
static int afs_dir_writepages(struct address_space *mapping,
			      struct writeback_control *wbc)
{
	struct afs_vnode *dvnode = AFS_FS_I(mapping->host);
	struct iov_iter iter;
	bool is_dir = (S_ISDIR(dvnode->netfs.inode.i_mode) &&
		       !test_bit(AFS_VNODE_MOUNTPOINT, &dvnode->flags));
	int ret = 0;

	/* Need to lock to prevent the folio queue and folios from being thrown
	 * away.
	 */
	if (!down_read_trylock(&dvnode->validate_lock)) {
		if (wbc->sync_mode == WB_SYNC_NONE) {
			/* The VFS will have undirtied the inode. */
			netfs_single_mark_inode_dirty(&dvnode->netfs.inode);
			return 0;
		}
		down_read(&dvnode->validate_lock);
	}

	if (is_dir ?
	    test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) :
	    atomic64_read(&dvnode->cb_expires_at) != AFS_NO_CB_PROMISE) {
	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
		iov_iter_folio_queue(&iter, ITER_SOURCE, dvnode->directory, 0, 0,
				     i_size_read(&dvnode->netfs.inode));
		ret = netfs_writeback_single(mapping, wbc, &iter);
		if (ret == 1)
			ret = 0; /* Skipped write due to lock conflict. */
	}

	up_read(&dvnode->validate_lock);
Loading