NFS client bugfixes for Linux 5.13

Highlights include
 
 Stable fixes:
 - Fix use-after-free in nfs4_init_client()
 
 Bugfixes:
 - Fix deadlock between nfs4_evict_inode() and nfs4_opendata_get_inode()
 - Fix second deadlock in nfs4_evict_inode()
 - nfs4_proc_set_acl should not change the value of NFS_CAP_UIDGID_NOMAP
 - Fix setting of the NFS_CAP_SECURITY_LABEL capability
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEESQctxSBg8JpV8KqEZwvnipYKAPIFAmDGJPEACgkQZwvnipYK
 APLH8xAAsdoKVCW35P+FtlzQvq0iWoTvk15i4Jv8+SyFtqAZe6y6pEj9+RT47CAV
 kt/uNa6CQ9KjxxgwBf2XoGTuf4MrOUU34kQBF/tRLy9zDdXUsZH263vapopmel6L
 BVHEEsID6hz8+BUt1LFsr+8sWxG+12UiimEu0CVo4BE8SgYushWpJOQ9iL/zxi1O
 gXmlAfA9g38I9aUApke4hOPSHVTGaQaAKl5LbSoycQlJblzgA1yIXdU9sVTHDJY6
 sco9O9M+NPY8gefS4d7iXSihZin5V9rNuSJ9SKiCPikTEjZYgZbw1umGj6VnF/5e
 QD47QGgOwXKeCOBv6Oe4VYxE2JISoUFZw8+pxjy4eDO+EcJv3IrHOM8UrsiddGAA
 DLHzbbrMUx6mGdgibw/ktkwx0Q/DvGrfrvKidk33cs16DPWgTZAG//n7spuqYTmT
 8fQbJF6DDjsYM7v+WdImf7VBA8dreXb/QcHwxCtH7uG+hGyRiYoDSOmH3mGBKpLX
 idkjz6Hvj7V7Y1z4qd+nvh4Ch1V0b9BX+J/+6dKHRykpmSJTIMIlQw7/wA6a8Lp6
 WJX4KbUzZHojvqM1BMzRL34+qidihUso0RIj0VjCB1JQyosRnIeTPorfHLQZTOM0
 IjP8h48BB7E7cJeJP1dmhvm7Hb8SpFVDxDHoWRtscbQflO3Wdkw=
 =PABi
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-5.13-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client bugfixes from Trond Myklebust:
 "Highlights include:

  Stable fixes:

   - Fix use-after-free in nfs4_init_client()

  Bugfixes:

   - Fix deadlock between nfs4_evict_inode() and nfs4_opendata_get_inode()

   - Fix second deadlock in nfs4_evict_inode()

   - nfs4_proc_set_acl should not change the value of NFS_CAP_UIDGID_NOMAP

   - Fix setting of the NFS_CAP_SECURITY_LABEL capability"

* tag 'nfs-for-5.13-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFSv4: Fix second deadlock in nfs4_evict_inode()
  NFSv4: Fix deadlock between nfs4_evict_inode() and nfs4_opendata_get_inode()
  NFS: FMODE_READ and friends are C macros, not enum types
  NFS: Fix a potential NULL dereference in nfs_get_client()
  NFS: Fix use-after-free in nfs4_init_client()
  NFS: Ensure the NFS_CAP_SECURITY_LABEL capability is set when appropriate
  NFSv4: nfs4_proc_set_acl needs to restore NFS_CAP_UIDGID_NOMAP on error.
This commit is contained in:
Linus Torvalds 2021-06-13 12:32:59 -07:00
commit 960f0716d8
5 changed files with 33 additions and 13 deletions

View File

@ -406,7 +406,7 @@ struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init)
if (cl_init->hostname == NULL) { if (cl_init->hostname == NULL) {
WARN_ON(1); WARN_ON(1);
return NULL; return ERR_PTR(-EINVAL);
} }
/* see if the client already exists */ /* see if the client already exists */

View File

@ -205,6 +205,7 @@ struct nfs4_exception {
struct inode *inode; struct inode *inode;
nfs4_stateid *stateid; nfs4_stateid *stateid;
long timeout; long timeout;
unsigned char task_is_privileged : 1;
unsigned char delay : 1, unsigned char delay : 1,
recovering : 1, recovering : 1,
retry : 1; retry : 1;

View File

@ -435,8 +435,8 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp,
*/ */
nfs_mark_client_ready(clp, -EPERM); nfs_mark_client_ready(clp, -EPERM);
} }
nfs_put_client(clp);
clear_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags); clear_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags);
nfs_put_client(clp);
return old; return old;
error: error:

View File

@ -589,6 +589,8 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_
goto out_retry; goto out_retry;
} }
if (exception->recovering) { if (exception->recovering) {
if (exception->task_is_privileged)
return -EDEADLOCK;
ret = nfs4_wait_clnt_recover(clp); ret = nfs4_wait_clnt_recover(clp);
if (test_bit(NFS_MIG_FAILED, &server->mig_status)) if (test_bit(NFS_MIG_FAILED, &server->mig_status))
return -EIO; return -EIO;
@ -614,6 +616,8 @@ nfs4_async_handle_exception(struct rpc_task *task, struct nfs_server *server,
goto out_retry; goto out_retry;
} }
if (exception->recovering) { if (exception->recovering) {
if (exception->task_is_privileged)
return -EDEADLOCK;
rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL); rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0) if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task); rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
@ -3878,6 +3882,10 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
server->caps |= NFS_CAP_HARDLINKS; server->caps |= NFS_CAP_HARDLINKS;
if (res.has_symlinks != 0) if (res.has_symlinks != 0)
server->caps |= NFS_CAP_SYMLINKS; server->caps |= NFS_CAP_SYMLINKS;
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
if (res.attr_bitmask[2] & FATTR4_WORD2_SECURITY_LABEL)
server->caps |= NFS_CAP_SECURITY_LABEL;
#endif
if (!(res.attr_bitmask[0] & FATTR4_WORD0_FILEID)) if (!(res.attr_bitmask[0] & FATTR4_WORD0_FILEID))
server->fattr_valid &= ~NFS_ATTR_FATTR_FILEID; server->fattr_valid &= ~NFS_ATTR_FATTR_FILEID;
if (!(res.attr_bitmask[1] & FATTR4_WORD1_MODE)) if (!(res.attr_bitmask[1] & FATTR4_WORD1_MODE))
@ -3898,10 +3906,6 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
server->fattr_valid &= ~NFS_ATTR_FATTR_CTIME; server->fattr_valid &= ~NFS_ATTR_FATTR_CTIME;
if (!(res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY)) if (!(res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY))
server->fattr_valid &= ~NFS_ATTR_FATTR_MTIME; server->fattr_valid &= ~NFS_ATTR_FATTR_MTIME;
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
if (!(res.attr_bitmask[2] & FATTR4_WORD2_SECURITY_LABEL))
server->fattr_valid &= ~NFS_ATTR_FATTR_V4_SECURITY_LABEL;
#endif
memcpy(server->attr_bitmask_nl, res.attr_bitmask, memcpy(server->attr_bitmask_nl, res.attr_bitmask,
sizeof(server->attr_bitmask)); sizeof(server->attr_bitmask));
server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL; server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
@ -5968,6 +5972,14 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
do { do {
err = __nfs4_proc_set_acl(inode, buf, buflen); err = __nfs4_proc_set_acl(inode, buf, buflen);
trace_nfs4_set_acl(inode, err); trace_nfs4_set_acl(inode, err);
if (err == -NFS4ERR_BADOWNER || err == -NFS4ERR_BADNAME) {
/*
* no need to retry since the kernel
* isn't involved in encoding the ACEs.
*/
err = -EINVAL;
break;
}
err = nfs4_handle_exception(NFS_SERVER(inode), err, err = nfs4_handle_exception(NFS_SERVER(inode), err,
&exception); &exception);
} while (exception.retry); } while (exception.retry);
@ -6409,6 +6421,7 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
struct nfs4_exception exception = { struct nfs4_exception exception = {
.inode = data->inode, .inode = data->inode,
.stateid = &data->stateid, .stateid = &data->stateid,
.task_is_privileged = data->args.seq_args.sa_privileged,
}; };
if (!nfs4_sequence_done(task, &data->res.seq_res)) if (!nfs4_sequence_done(task, &data->res.seq_res))
@ -6532,7 +6545,6 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
data = kzalloc(sizeof(*data), GFP_NOFS); data = kzalloc(sizeof(*data), GFP_NOFS);
if (data == NULL) if (data == NULL)
return -ENOMEM; return -ENOMEM;
nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, 0);
nfs4_state_protect(server->nfs_client, nfs4_state_protect(server->nfs_client,
NFS_SP4_MACH_CRED_CLEANUP, NFS_SP4_MACH_CRED_CLEANUP,
@ -6563,6 +6575,12 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
} }
} }
if (!data->inode)
nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1,
1);
else
nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1,
0);
task_setup_data.callback_data = data; task_setup_data.callback_data = data;
msg.rpc_argp = &data->args; msg.rpc_argp = &data->args;
msg.rpc_resp = &data->res; msg.rpc_resp = &data->res;
@ -9640,15 +9658,20 @@ int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp, bool sync)
&task_setup_data.rpc_client, &msg); &task_setup_data.rpc_client, &msg);
dprintk("--> %s\n", __func__); dprintk("--> %s\n", __func__);
lrp->inode = nfs_igrab_and_active(lrp->args.inode);
if (!sync) { if (!sync) {
lrp->inode = nfs_igrab_and_active(lrp->args.inode);
if (!lrp->inode) { if (!lrp->inode) {
nfs4_layoutreturn_release(lrp); nfs4_layoutreturn_release(lrp);
return -EAGAIN; return -EAGAIN;
} }
task_setup_data.flags |= RPC_TASK_ASYNC; task_setup_data.flags |= RPC_TASK_ASYNC;
} }
nfs4_init_sequence(&lrp->args.seq_args, &lrp->res.seq_res, 1, 0); if (!lrp->inode)
nfs4_init_sequence(&lrp->args.seq_args, &lrp->res.seq_res, 1,
1);
else
nfs4_init_sequence(&lrp->args.seq_args, &lrp->res.seq_res, 1,
0);
task = rpc_run_task(&task_setup_data); task = rpc_run_task(&task_setup_data);
if (IS_ERR(task)) if (IS_ERR(task))
return PTR_ERR(task); return PTR_ERR(task);

View File

@ -430,10 +430,6 @@ TRACE_DEFINE_ENUM(O_CLOEXEC);
{ O_NOATIME, "O_NOATIME" }, \ { O_NOATIME, "O_NOATIME" }, \
{ O_CLOEXEC, "O_CLOEXEC" }) { O_CLOEXEC, "O_CLOEXEC" })
TRACE_DEFINE_ENUM(FMODE_READ);
TRACE_DEFINE_ENUM(FMODE_WRITE);
TRACE_DEFINE_ENUM(FMODE_EXEC);
#define show_fmode_flags(mode) \ #define show_fmode_flags(mode) \
__print_flags(mode, "|", \ __print_flags(mode, "|", \
{ ((__force unsigned long)FMODE_READ), "READ" }, \ { ((__force unsigned long)FMODE_READ), "READ" }, \