Commit f0b9d8eb authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull nfsd fixes from Chuck Lever:
 "A set of NFSD fixes for stable that arrived after the merge window:

   - Remove an invalid NFS status code

   - Fix an fstests failure when using pNFS

   - Fix a UAF in v4_end_grace()

   - Fix the administrative interface used to revoke NFSv4 state

   - Fix a memory leak reported by syzbot"

* tag 'nfsd-6.19-3' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux:
  NFSD: net ref data still needs to be freed even if net hasn't startup
  nfsd: check that server is running in unlock_filesystem
  nfsd: use correct loop termination in nfsd4_revoke_states()
  nfsd: provide locking for v4_end_grace
  NFSD: Fix permission check for read access to executable-only files
  NFSD: Remove NFSERR_EAGAIN
parents 7f98ab9d 0b88bfa4
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@ static const struct {
	{ NFSERR_NOENT,		-ENOENT		},
	{ NFSERR_IO,		-EIO		},
	{ NFSERR_NXIO,		-ENXIO		},
/*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
	{ NFSERR_ACCES,		-EACCES		},
	{ NFSERR_EXIST,		-EEXIST		},
	{ NFSERR_XDEV,		-EXDEV		},
+2 −0
Original line number Diff line number Diff line
@@ -66,6 +66,8 @@ struct nfsd_net {

	struct lock_manager nfsd4_manager;
	bool grace_ended;
	bool grace_end_forced;
	bool client_tracking_active;
	time64_t boot_time;

	struct dentry *nfsd_client_dir;
+1 −1
Original line number Diff line number Diff line
@@ -1502,7 +1502,7 @@ static __be32 nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr,
					(schedule_timeout(20*HZ) == 0)) {
				finish_wait(&nn->nfsd_ssc_waitq, &wait);
				kfree(work);
				return nfserr_eagain;
				return nfserr_jukebox;
			}
			finish_wait(&nn->nfsd_ssc_waitq, &wait);
			goto try_again;
+43 −6
Original line number Diff line number Diff line
@@ -84,7 +84,7 @@ static u64 current_sessionid = 1;
/* forward declarations */
static bool check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner);
static void nfs4_free_ol_stateid(struct nfs4_stid *stid);
void nfsd4_end_grace(struct nfsd_net *nn);
static void nfsd4_end_grace(struct nfsd_net *nn);
static void _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps);
static void nfsd4_file_hash_remove(struct nfs4_file *fi);
static void deleg_reaper(struct nfsd_net *nn);
@@ -1759,7 +1759,7 @@ static struct nfs4_stid *find_one_sb_stid(struct nfs4_client *clp,

/**
 * nfsd4_revoke_states - revoke all nfsv4 states associated with given filesystem
 * @net:  used to identify instance of nfsd (there is one per net namespace)
 * @nn:   used to identify instance of nfsd (there is one per net namespace)
 * @sb:   super_block used to identify target filesystem
 *
 * All nfs4 states (open, lock, delegation, layout) held by the server instance
@@ -1771,16 +1771,15 @@ static struct nfs4_stid *find_one_sb_stid(struct nfs4_client *clp,
 * The clients which own the states will subsequently being notified that the
 * states have been "admin-revoked".
 */
void nfsd4_revoke_states(struct net *net, struct super_block *sb)
void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
{
	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
	unsigned int idhashval;
	unsigned int sc_types;

	sc_types = SC_TYPE_OPEN | SC_TYPE_LOCK | SC_TYPE_DELEG | SC_TYPE_LAYOUT;

	spin_lock(&nn->client_lock);
	for (idhashval = 0; idhashval < CLIENT_HASH_MASK; idhashval++) {
	for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) {
		struct list_head *head = &nn->conf_id_hashtbl[idhashval];
		struct nfs4_client *clp;
	retry:
@@ -6570,7 +6569,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	return nfs_ok;
}

void
static void
nfsd4_end_grace(struct nfsd_net *nn)
{
	/* do nothing if grace period already ended */
@@ -6603,6 +6602,33 @@ nfsd4_end_grace(struct nfsd_net *nn)
	 */
}

/**
 * nfsd4_force_end_grace - forcibly end the NFSv4 grace period
 * @nn: network namespace for the server instance to be updated
 *
 * Forces bypass of normal grace period completion, then schedules
 * the laundromat to end the grace period immediately. Does not wait
 * for the grace period to fully terminate before returning.
 *
 * Return values:
 *   %true: Grace termination schedule
 *   %false: No action was taken
 */
bool nfsd4_force_end_grace(struct nfsd_net *nn)
{
	if (!nn->client_tracking_ops)
		return false;
	spin_lock(&nn->client_lock);
	if (nn->grace_ended || !nn->client_tracking_active) {
		spin_unlock(&nn->client_lock);
		return false;
	}
	WRITE_ONCE(nn->grace_end_forced, true);
	mod_delayed_work(laundry_wq, &nn->laundromat_work, 0);
	spin_unlock(&nn->client_lock);
	return true;
}

/*
 * If we've waited a lease period but there are still clients trying to
 * reclaim, wait a little longer to give them a chance to finish.
@@ -6612,6 +6638,8 @@ static bool clients_still_reclaiming(struct nfsd_net *nn)
	time64_t double_grace_period_end = nn->boot_time +
					   2 * nn->nfsd4_lease;

	if (READ_ONCE(nn->grace_end_forced))
		return false;
	if (nn->track_reclaim_completes &&
			atomic_read(&nn->nr_reclaim_complete) ==
			nn->reclaim_str_hashtbl_size)
@@ -8932,6 +8960,8 @@ static int nfs4_state_create_net(struct net *net)
	nn->unconf_name_tree = RB_ROOT;
	nn->boot_time = ktime_get_real_seconds();
	nn->grace_ended = false;
	nn->grace_end_forced = false;
	nn->client_tracking_active = false;
	nn->nfsd4_manager.block_opens = true;
	INIT_LIST_HEAD(&nn->nfsd4_manager.list);
	INIT_LIST_HEAD(&nn->client_lru);
@@ -9012,6 +9042,10 @@ nfs4_state_start_net(struct net *net)
		return ret;
	locks_start_grace(net, &nn->nfsd4_manager);
	nfsd4_client_tracking_init(net);
	/* safe for laundromat to run now */
	spin_lock(&nn->client_lock);
	nn->client_tracking_active = true;
	spin_unlock(&nn->client_lock);
	if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0)
		goto skip_grace;
	printk(KERN_INFO "NFSD: starting %lld-second grace period (net %x)\n",
@@ -9060,6 +9094,9 @@ nfs4_state_shutdown_net(struct net *net)

	shrinker_free(nn->nfsd_client_shrinker);
	cancel_work_sync(&nn->nfsd_shrinker_work);
	spin_lock(&nn->client_lock);
	nn->client_tracking_active = false;
	spin_unlock(&nn->client_lock);
	cancel_delayed_work_sync(&nn->laundromat_work);
	locks_end_grace(&nn->nfsd4_manager);

+9 −3
Original line number Diff line number Diff line
@@ -259,6 +259,7 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
	struct path path;
	char *fo_path;
	int error;
	struct nfsd_net *nn;

	/* sanity check */
	if (size == 0)
@@ -285,7 +286,13 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
	 * 3.  Is that directory the root of an exported file system?
	 */
	error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
	nfsd4_revoke_states(netns(file), path.dentry->d_sb);
	mutex_lock(&nfsd_mutex);
	nn = net_generic(netns(file), nfsd_net_id);
	if (nn->nfsd_serv)
		nfsd4_revoke_states(nn, path.dentry->d_sb);
	else
		error = -EINVAL;
	mutex_unlock(&nfsd_mutex);

	path_put(&path);
	return error;
@@ -1082,10 +1089,9 @@ static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size)
		case 'Y':
		case 'y':
		case '1':
			if (!nn->nfsd_serv)
			if (!nfsd4_force_end_grace(nn))
				return -EBUSY;
			trace_nfsd_end_grace(netns(file));
			nfsd4_end_grace(nn);
			break;
		default:
			return -EINVAL;
Loading