Commit e609571b authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull NFS client fixes from Trond Myklebust:
 "Highlights include:

   - Fix parsing of link-local IPv6 addresses

   - Fix confusing logging of mount errors that was introduced by the
     fsopen() patchset.

   - Fix a tracing use after free in _nfs4_do_setlk()

   - Layout return-on-close fixes when called from nfs4_evict_inode()

   - Layout segments were being leaked in
     pnfs_generic_clear_request_commit()

   - Don't leak DS commits in pnfs_generic_retry_commit()

   - Fix an Oopsable use-after-free when nfs_delegation_find_inode_server()
     calls iput() on an inode after the super block has gone away"

* tag 'nfs-for-5.11-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS: nfs_igrab_and_active must first reference the superblock
  NFS: nfs_delegation_find_inode_server must first reference the superblock
  NFS/pNFS: Fix a leak of the layout 'plh_outstanding' counter
  NFS/pNFS: Don't leak DS commits in pnfs_generic_retry_commit()
  NFS/pNFS: Don't call pnfs_free_bucket_lseg() before removing the request
  pNFS: Stricter ordering of layoutget and layoutreturn
  pNFS: Clean up pnfs_layoutreturn_free_lsegs()
  pNFS: We want return-on-close to complete when evicting the inode
  pNFS: Mark layout for return if return-on-close was not sent
  net: sunrpc: interpret the return value of kstrtou32 correctly
  NFS: Adjust fs_context error logging
  NFS4: Fix use-after-free in trace_event_raw_event_nfs4_set_lock
parents ea49c88f 896567ee
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -1011,22 +1011,24 @@ nfs_delegation_find_inode_server(struct nfs_server *server,
				 const struct nfs_fh *fhandle)
{
	struct nfs_delegation *delegation;
	struct inode *freeme, *res = NULL;
	struct super_block *freeme = NULL;
	struct inode *res = NULL;

	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
		spin_lock(&delegation->lock);
		if (delegation->inode != NULL &&
		    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
		    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
			freeme = igrab(delegation->inode);
			if (freeme && nfs_sb_active(freeme->i_sb))
				res = freeme;
			if (nfs_sb_active(server->super)) {
				freeme = server->super;
				res = igrab(delegation->inode);
			}
			spin_unlock(&delegation->lock);
			if (res != NULL)
				return res;
			if (freeme) {
				rcu_read_unlock();
				iput(freeme);
				nfs_sb_deactive(freeme);
				rcu_read_lock();
			}
			return ERR_PTR(-EAGAIN);
+30 −8
Original line number Diff line number Diff line
@@ -136,9 +136,29 @@ struct nfs_fs_context {
	} clone_data;
};

#define nfs_errorf(fc, fmt, ...) errorf(fc, fmt, ## __VA_ARGS__)
#define nfs_invalf(fc, fmt, ...) invalf(fc, fmt, ## __VA_ARGS__)
#define nfs_warnf(fc, fmt, ...) warnf(fc, fmt, ## __VA_ARGS__)
#define nfs_errorf(fc, fmt, ...) ((fc)->log.log ?		\
	errorf(fc, fmt, ## __VA_ARGS__) :			\
	({ dprintk(fmt "\n", ## __VA_ARGS__); }))

#define nfs_ferrorf(fc, fac, fmt, ...) ((fc)->log.log ?		\
	errorf(fc, fmt, ## __VA_ARGS__) :			\
	({ dfprintk(fac, fmt "\n", ## __VA_ARGS__); }))

#define nfs_invalf(fc, fmt, ...) ((fc)->log.log ?		\
	invalf(fc, fmt, ## __VA_ARGS__) :			\
	({ dprintk(fmt "\n", ## __VA_ARGS__);  -EINVAL; }))

#define nfs_finvalf(fc, fac, fmt, ...) ((fc)->log.log ?		\
	invalf(fc, fmt, ## __VA_ARGS__) :			\
	({ dfprintk(fac, fmt "\n", ## __VA_ARGS__);  -EINVAL; }))

#define nfs_warnf(fc, fmt, ...) ((fc)->log.log ?		\
	warnf(fc, fmt, ## __VA_ARGS__) :			\
	({ dprintk(fmt "\n", ## __VA_ARGS__); }))

#define nfs_fwarnf(fc, fac, fmt, ...) ((fc)->log.log ?		\
	warnf(fc, fmt, ## __VA_ARGS__) :			\
	({ dfprintk(fac, fmt "\n", ## __VA_ARGS__); }))

static inline struct nfs_fs_context *nfs_fc2context(const struct fs_context *fc)
{
@@ -579,12 +599,14 @@ extern void nfs4_test_session_trunk(struct rpc_clnt *clnt,

static inline struct inode *nfs_igrab_and_active(struct inode *inode)
{
	inode = igrab(inode);
	if (inode != NULL && !nfs_sb_active(inode->i_sb)) {
		iput(inode);
		inode = NULL;
	}
	struct super_block *sb = inode->i_sb;

	if (sb && nfs_sb_active(sb)) {
		if (igrab(inode))
			return inode;
		nfs_sb_deactive(sb);
	}
	return NULL;
}

static inline void nfs_iput_and_deactive(struct inode *inode)
+11 −17
Original line number Diff line number Diff line
@@ -3536,9 +3536,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
	trace_nfs4_close(state, &calldata->arg, &calldata->res, task->tk_status);

	/* Handle Layoutreturn errors */
	if (pnfs_roc_done(task, calldata->inode,
				&calldata->arg.lr_args,
				&calldata->res.lr_res,
	if (pnfs_roc_done(task, &calldata->arg.lr_args, &calldata->res.lr_res,
			  &calldata->res.lr_ret) == -EAGAIN)
		goto out_restart;

@@ -6384,9 +6382,7 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
	trace_nfs4_delegreturn_exit(&data->args, &data->res, task->tk_status);

	/* Handle Layoutreturn errors */
	if (pnfs_roc_done(task, data->inode,
				&data->args.lr_args,
				&data->res.lr_res,
	if (pnfs_roc_done(task, &data->args.lr_args, &data->res.lr_res,
			  &data->res.lr_ret) == -EAGAIN)
		goto out_restart;

@@ -6441,10 +6437,10 @@ static void nfs4_delegreturn_release(void *calldata)
	struct nfs4_delegreturndata *data = calldata;
	struct inode *inode = data->inode;

	if (inode) {
	if (data->lr.roc)
		pnfs_roc_release(&data->lr.arg, &data->lr.res,
				 data->res.lr_ret);
	if (inode) {
		nfs_post_op_update_inode_force_wcc(inode, &data->fattr);
		nfs_iput_and_deactive(inode);
	}
@@ -6520,16 +6516,14 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
	nfs_fattr_init(data->res.fattr);
	data->timestamp = jiffies;
	data->rpc_status = 0;
	data->lr.roc = pnfs_roc(inode, &data->lr.arg, &data->lr.res, cred);
	data->inode = nfs_igrab_and_active(inode);
	if (data->inode) {
	if (data->inode || issync) {
		data->lr.roc = pnfs_roc(inode, &data->lr.arg, &data->lr.res,
					cred);
		if (data->lr.roc) {
			data->args.lr_args = &data->lr.arg;
			data->res.lr_res = &data->lr.res;
		}
	} else if (data->lr.roc) {
		pnfs_roc_release(&data->lr.arg, &data->lr.res, 0);
		data->lr.roc = false;
	}

	task_setup_data.callback_data = data;
@@ -7111,9 +7105,9 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
					data->arg.new_lock_owner, ret);
	} else
		data->cancelled = true;
	trace_nfs4_set_lock(fl, state, &data->res.stateid, cmd, ret);
	rpc_put_task(task);
	dprintk("%s: done, ret = %d!\n", __func__, ret);
	trace_nfs4_set_lock(fl, state, &data->res.stateid, cmd, ret);
	return ret;
}

+2 −2
Original line number Diff line number Diff line
@@ -227,7 +227,7 @@ int nfs4_try_get_tree(struct fs_context *fc)
			   fc, ctx->nfs_server.hostname,
			   ctx->nfs_server.export_path);
	if (err) {
		nfs_errorf(fc, "NFS4: Couldn't follow remote path");
		nfs_ferrorf(fc, MOUNT, "NFS4: Couldn't follow remote path");
		dfprintk(MOUNT, "<-- nfs4_try_get_tree() = %d [error]\n", err);
	} else {
		dfprintk(MOUNT, "<-- nfs4_try_get_tree() = 0\n");
@@ -250,7 +250,7 @@ int nfs4_get_referral_tree(struct fs_context *fc)
			    fc, ctx->nfs_server.hostname,
			    ctx->nfs_server.export_path);
	if (err) {
		nfs_errorf(fc, "NFS4: Couldn't follow remote path");
		nfs_ferrorf(fc, MOUNT, "NFS4: Couldn't follow remote path");
		dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = %d [error]\n", err);
	} else {
		dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = 0\n");
+35 −32
Original line number Diff line number Diff line
@@ -1152,7 +1152,7 @@ void pnfs_layoutreturn_free_lsegs(struct pnfs_layout_hdr *lo,
	LIST_HEAD(freeme);

	spin_lock(&inode->i_lock);
	if (!pnfs_layout_is_valid(lo) || !arg_stateid ||
	if (!pnfs_layout_is_valid(lo) ||
	    !nfs4_stateid_match_other(&lo->plh_stateid, arg_stateid))
		goto out_unlock;
	if (stateid) {
@@ -1509,10 +1509,8 @@ bool pnfs_roc(struct inode *ino,
	return false;
}

int pnfs_roc_done(struct rpc_task *task, struct inode *inode,
		struct nfs4_layoutreturn_args **argpp,
		struct nfs4_layoutreturn_res **respp,
		int *ret)
int pnfs_roc_done(struct rpc_task *task, struct nfs4_layoutreturn_args **argpp,
		  struct nfs4_layoutreturn_res **respp, int *ret)
{
	struct nfs4_layoutreturn_args *arg = *argpp;
	int retval = -EAGAIN;
@@ -1545,7 +1543,7 @@ int pnfs_roc_done(struct rpc_task *task, struct inode *inode,
		return 0;
	case -NFS4ERR_OLD_STATEID:
		if (!nfs4_layout_refresh_old_stateid(&arg->stateid,
					&arg->range, inode))
						     &arg->range, arg->inode))
			break;
		*ret = -NFS4ERR_NOMATCHING_LAYOUT;
		return -EAGAIN;
@@ -1560,23 +1558,28 @@ void pnfs_roc_release(struct nfs4_layoutreturn_args *args,
		int ret)
{
	struct pnfs_layout_hdr *lo = args->layout;
	const nfs4_stateid *arg_stateid = NULL;
	struct inode *inode = args->inode;
	const nfs4_stateid *res_stateid = NULL;
	struct nfs4_xdr_opaque_data *ld_private = args->ld_private;

	switch (ret) {
	case -NFS4ERR_NOMATCHING_LAYOUT:
		spin_lock(&inode->i_lock);
		if (pnfs_layout_is_valid(lo) &&
		    nfs4_stateid_match_other(&args->stateid, &lo->plh_stateid))
			pnfs_set_plh_return_info(lo, args->range.iomode, 0);
		pnfs_clear_layoutreturn_waitbit(lo);
		spin_unlock(&inode->i_lock);
		break;
	case 0:
		if (res->lrs_present)
			res_stateid = &res->stateid;
		fallthrough;
	default:
		arg_stateid = &args->stateid;
		pnfs_layoutreturn_free_lsegs(lo, &args->stateid, &args->range,
					     res_stateid);
	}
	trace_nfs4_layoutreturn_on_close(args->inode, &args->stateid, ret);
	pnfs_layoutreturn_free_lsegs(lo, arg_stateid, &args->range,
			res_stateid);
	if (ld_private && ld_private->ops && ld_private->ops->free)
		ld_private->ops->free(ld_private);
	pnfs_put_layout_hdr(lo);
@@ -2015,6 +2018,27 @@ pnfs_update_layout(struct inode *ino,
		goto lookup_again;
	}

	/*
	 * Because we free lsegs when sending LAYOUTRETURN, we need to wait
	 * for LAYOUTRETURN.
	 */
	if (test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) {
		spin_unlock(&ino->i_lock);
		dprintk("%s wait for layoutreturn\n", __func__);
		lseg = ERR_PTR(pnfs_prepare_to_retry_layoutget(lo));
		if (!IS_ERR(lseg)) {
			pnfs_put_layout_hdr(lo);
			dprintk("%s retrying\n", __func__);
			trace_pnfs_update_layout(ino, pos, count, iomode, lo,
						 lseg,
						 PNFS_UPDATE_LAYOUT_RETRY);
			goto lookup_again;
		}
		trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
					 PNFS_UPDATE_LAYOUT_RETURN);
		goto out_put_layout_hdr;
	}

	lseg = pnfs_find_lseg(lo, &arg, strict_iomode);
	if (lseg) {
		trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
@@ -2067,28 +2091,6 @@ pnfs_update_layout(struct inode *ino,
		nfs4_stateid_copy(&stateid, &lo->plh_stateid);
	}

	/*
	 * Because we free lsegs before sending LAYOUTRETURN, we need to wait
	 * for LAYOUTRETURN even if first is true.
	 */
	if (test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) {
		spin_unlock(&ino->i_lock);
		dprintk("%s wait for layoutreturn\n", __func__);
		lseg = ERR_PTR(pnfs_prepare_to_retry_layoutget(lo));
		if (!IS_ERR(lseg)) {
			if (first)
				pnfs_clear_first_layoutget(lo);
			pnfs_put_layout_hdr(lo);
			dprintk("%s retrying\n", __func__);
			trace_pnfs_update_layout(ino, pos, count, iomode, lo,
					lseg, PNFS_UPDATE_LAYOUT_RETRY);
			goto lookup_again;
		}
		trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
				PNFS_UPDATE_LAYOUT_RETURN);
		goto out_put_layout_hdr;
	}

	if (pnfs_layoutgets_blocked(lo)) {
		trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
				PNFS_UPDATE_LAYOUT_BLOCKED);
@@ -2242,6 +2244,7 @@ static void _lgopen_prepare_attached(struct nfs4_opendata *data,
					     &rng, GFP_KERNEL);
	if (!lgp) {
		pnfs_clear_first_layoutget(lo);
		nfs_layoutget_end(lo);
		pnfs_put_layout_hdr(lo);
		return;
	}
Loading