Commit e95003c3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-3.14-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client bugfixes from Trond Myklebust:
 "Highlights include stable fixes for the following bugs:

   - General performance regression due to NFS_INO_INVALID_LABEL being
     set when the server doesn't support labeled NFS
   - Hang in the RPC code due to a socket out-of-buffer race
   - Infinite loop when trying to establish the NFSv4 lease
   - Use-after-free bug in the RPCSEC gss code.
   - nfs4_select_rw_stateid is returning with a non-zero error value on
     success

  Other bug fixes:

  - Potential memory scribble in the RPC bi-directional RPC code
  - Pipe version reference leak
  - Use the correct net namespace in the new NFSv4 migration code"

* tag 'nfs-for-3.14-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS fix error return in nfs4_select_rw_stateid
  NFSv4: Use the correct net namespace in nfs4_update_server
  SUNRPC: Fix a pipe_version reference leak
  SUNRPC: Ensure that gss_auth isn't freed before its upcall messages
  SUNRPC: Fix potential memory scribble in xprt_free_bc_request()
  SUNRPC: Fix races in xs_nospace()
  SUNRPC: Don't create a gss auth cache unless rpc.gssd is running
  NFS: Do not set NFS_INO_INVALID_LABEL unless server supports labeled NFS
parents 981adacd 146d70ca
...@@ -164,17 +164,16 @@ static void nfs_zap_caches_locked(struct inode *inode) ...@@ -164,17 +164,16 @@ static void nfs_zap_caches_locked(struct inode *inode)
if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) { if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) {
nfs_fscache_invalidate(inode); nfs_fscache_invalidate(inode);
nfsi->cache_validity |= NFS_INO_INVALID_ATTR nfsi->cache_validity |= NFS_INO_INVALID_ATTR
| NFS_INO_INVALID_LABEL
| NFS_INO_INVALID_DATA | NFS_INO_INVALID_DATA
| NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL | NFS_INO_INVALID_ACL
| NFS_INO_REVAL_PAGECACHE; | NFS_INO_REVAL_PAGECACHE;
} else } else
nfsi->cache_validity |= NFS_INO_INVALID_ATTR nfsi->cache_validity |= NFS_INO_INVALID_ATTR
| NFS_INO_INVALID_LABEL
| NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL | NFS_INO_INVALID_ACL
| NFS_INO_REVAL_PAGECACHE; | NFS_INO_REVAL_PAGECACHE;
nfs_zap_label_cache_locked(nfsi);
} }
void nfs_zap_caches(struct inode *inode) void nfs_zap_caches(struct inode *inode)
...@@ -266,6 +265,13 @@ nfs_init_locked(struct inode *inode, void *opaque) ...@@ -266,6 +265,13 @@ nfs_init_locked(struct inode *inode, void *opaque)
} }
#ifdef CONFIG_NFS_V4_SECURITY_LABEL #ifdef CONFIG_NFS_V4_SECURITY_LABEL
static void nfs_clear_label_invalid(struct inode *inode)
{
spin_lock(&inode->i_lock);
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_LABEL;
spin_unlock(&inode->i_lock);
}
void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr, void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
struct nfs4_label *label) struct nfs4_label *label)
{ {
...@@ -283,6 +289,7 @@ void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr, ...@@ -283,6 +289,7 @@ void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
__func__, __func__,
(char *)label->label, (char *)label->label,
label->len, error); label->len, error);
nfs_clear_label_invalid(inode);
} }
} }
...@@ -1648,7 +1655,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1648,7 +1655,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
inode->i_blocks = fattr->du.nfs2.blocks; inode->i_blocks = fattr->du.nfs2.blocks;
/* Update attrtimeo value if we're out of the unstable period */ /* Update attrtimeo value if we're out of the unstable period */
if (invalid & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) { if (invalid & NFS_INO_INVALID_ATTR) {
nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE); nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = now; nfsi->attrtimeo_timestamp = now;
...@@ -1661,7 +1668,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1661,7 +1668,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
} }
} }
invalid &= ~NFS_INO_INVALID_ATTR; invalid &= ~NFS_INO_INVALID_ATTR;
invalid &= ~NFS_INO_INVALID_LABEL;
/* Don't invalidate the data if we were to blame */ /* Don't invalidate the data if we were to blame */
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
|| S_ISLNK(inode->i_mode))) || S_ISLNK(inode->i_mode)))
......
...@@ -176,7 +176,8 @@ extern struct nfs_server *nfs4_create_server( ...@@ -176,7 +176,8 @@ extern struct nfs_server *nfs4_create_server(
extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *, extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *,
struct nfs_fh *); struct nfs_fh *);
extern int nfs4_update_server(struct nfs_server *server, const char *hostname, extern int nfs4_update_server(struct nfs_server *server, const char *hostname,
struct sockaddr *sap, size_t salen); struct sockaddr *sap, size_t salen,
struct net *net);
extern void nfs_free_server(struct nfs_server *server); extern void nfs_free_server(struct nfs_server *server);
extern struct nfs_server *nfs_clone_server(struct nfs_server *, extern struct nfs_server *nfs_clone_server(struct nfs_server *,
struct nfs_fh *, struct nfs_fh *,
...@@ -279,9 +280,18 @@ static inline void nfs4_label_free(struct nfs4_label *label) ...@@ -279,9 +280,18 @@ static inline void nfs4_label_free(struct nfs4_label *label)
} }
return; return;
} }
static inline void nfs_zap_label_cache_locked(struct nfs_inode *nfsi)
{
if (nfs_server_capable(&nfsi->vfs_inode, NFS_CAP_SECURITY_LABEL))
nfsi->cache_validity |= NFS_INO_INVALID_LABEL;
}
#else #else
static inline struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags) { return NULL; } static inline struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags) { return NULL; }
static inline void nfs4_label_free(void *label) {} static inline void nfs4_label_free(void *label) {}
static inline void nfs_zap_label_cache_locked(struct nfs_inode *nfsi)
{
}
#endif /* CONFIG_NFS_V4_SECURITY_LABEL */ #endif /* CONFIG_NFS_V4_SECURITY_LABEL */
/* proc.c */ /* proc.c */
......
...@@ -1135,6 +1135,7 @@ static int nfs_probe_destination(struct nfs_server *server) ...@@ -1135,6 +1135,7 @@ static int nfs_probe_destination(struct nfs_server *server)
* @hostname: new end-point's hostname * @hostname: new end-point's hostname
* @sap: new end-point's socket address * @sap: new end-point's socket address
* @salen: size of "sap" * @salen: size of "sap"
* @net: net namespace
* *
* The nfs_server must be quiescent before this function is invoked. * The nfs_server must be quiescent before this function is invoked.
* Either its session is drained (NFSv4.1+), or its transport is * Either its session is drained (NFSv4.1+), or its transport is
...@@ -1143,13 +1144,13 @@ static int nfs_probe_destination(struct nfs_server *server) ...@@ -1143,13 +1144,13 @@ static int nfs_probe_destination(struct nfs_server *server)
* Returns zero on success, or a negative errno value. * Returns zero on success, or a negative errno value.
*/ */
int nfs4_update_server(struct nfs_server *server, const char *hostname, int nfs4_update_server(struct nfs_server *server, const char *hostname,
struct sockaddr *sap, size_t salen) struct sockaddr *sap, size_t salen, struct net *net)
{ {
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
struct rpc_clnt *clnt = server->client; struct rpc_clnt *clnt = server->client;
struct xprt_create xargs = { struct xprt_create xargs = {
.ident = clp->cl_proto, .ident = clp->cl_proto,
.net = &init_net, .net = net,
.dstaddr = sap, .dstaddr = sap,
.addrlen = salen, .addrlen = salen,
.servername = hostname, .servername = hostname,
...@@ -1189,7 +1190,7 @@ int nfs4_update_server(struct nfs_server *server, const char *hostname, ...@@ -1189,7 +1190,7 @@ int nfs4_update_server(struct nfs_server *server, const char *hostname,
error = nfs4_set_client(server, hostname, sap, salen, buf, error = nfs4_set_client(server, hostname, sap, salen, buf,
clp->cl_rpcclient->cl_auth->au_flavor, clp->cl_rpcclient->cl_auth->au_flavor,
clp->cl_proto, clnt->cl_timeout, clp->cl_proto, clnt->cl_timeout,
clp->cl_minorversion, clp->cl_net); clp->cl_minorversion, net);
nfs_put_client(clp); nfs_put_client(clp);
if (error != 0) { if (error != 0) {
nfs_server_insert_lists(server); nfs_server_insert_lists(server);
......
...@@ -121,9 +121,8 @@ static int nfs4_validate_fspath(struct dentry *dentry, ...@@ -121,9 +121,8 @@ static int nfs4_validate_fspath(struct dentry *dentry,
} }
static size_t nfs_parse_server_name(char *string, size_t len, static size_t nfs_parse_server_name(char *string, size_t len,
struct sockaddr *sa, size_t salen, struct nfs_server *server) struct sockaddr *sa, size_t salen, struct net *net)
{ {
struct net *net = rpc_net_ns(server->client);
ssize_t ret; ssize_t ret;
ret = rpc_pton(net, string, len, sa, salen); ret = rpc_pton(net, string, len, sa, salen);
...@@ -223,6 +222,7 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, ...@@ -223,6 +222,7 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
const struct nfs4_fs_location *location) const struct nfs4_fs_location *location)
{ {
const size_t addr_bufsize = sizeof(struct sockaddr_storage); const size_t addr_bufsize = sizeof(struct sockaddr_storage);
struct net *net = rpc_net_ns(NFS_SB(mountdata->sb)->client);
struct vfsmount *mnt = ERR_PTR(-ENOENT); struct vfsmount *mnt = ERR_PTR(-ENOENT);
char *mnt_path; char *mnt_path;
unsigned int maxbuflen; unsigned int maxbuflen;
...@@ -248,8 +248,7 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, ...@@ -248,8 +248,7 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
continue; continue;
mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len, mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len,
mountdata->addr, addr_bufsize, mountdata->addr, addr_bufsize, net);
NFS_SB(mountdata->sb));
if (mountdata->addrlen == 0) if (mountdata->addrlen == 0)
continue; continue;
...@@ -419,6 +418,7 @@ static int nfs4_try_replacing_one_location(struct nfs_server *server, ...@@ -419,6 +418,7 @@ static int nfs4_try_replacing_one_location(struct nfs_server *server,
const struct nfs4_fs_location *location) const struct nfs4_fs_location *location)
{ {
const size_t addr_bufsize = sizeof(struct sockaddr_storage); const size_t addr_bufsize = sizeof(struct sockaddr_storage);
struct net *net = rpc_net_ns(server->client);
struct sockaddr *sap; struct sockaddr *sap;
unsigned int s; unsigned int s;
size_t salen; size_t salen;
...@@ -440,7 +440,7 @@ static int nfs4_try_replacing_one_location(struct nfs_server *server, ...@@ -440,7 +440,7 @@ static int nfs4_try_replacing_one_location(struct nfs_server *server,
continue; continue;
salen = nfs_parse_server_name(buf->data, buf->len, salen = nfs_parse_server_name(buf->data, buf->len,
sap, addr_bufsize, server); sap, addr_bufsize, net);
if (salen == 0) if (salen == 0)
continue; continue;
rpc_set_port(sap, NFS_PORT); rpc_set_port(sap, NFS_PORT);
...@@ -450,7 +450,7 @@ static int nfs4_try_replacing_one_location(struct nfs_server *server, ...@@ -450,7 +450,7 @@ static int nfs4_try_replacing_one_location(struct nfs_server *server,
if (hostname == NULL) if (hostname == NULL)
break; break;
error = nfs4_update_server(server, hostname, sap, salen); error = nfs4_update_server(server, hostname, sap, salen, net);
kfree(hostname); kfree(hostname);
if (error == 0) if (error == 0)
break; break;
......
...@@ -1015,8 +1015,11 @@ int nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state, ...@@ -1015,8 +1015,11 @@ int nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state,
if (ret == -EIO) if (ret == -EIO)
/* A lost lock - don't even consider delegations */ /* A lost lock - don't even consider delegations */
goto out; goto out;
if (nfs4_copy_delegation_stateid(dst, state->inode, fmode)) /* returns true if delegation stateid found and copied */
if (nfs4_copy_delegation_stateid(dst, state->inode, fmode)) {
ret = 0;
goto out; goto out;
}
if (ret != -ENOENT) if (ret != -ENOENT)
/* nfs4_copy_delegation_stateid() didn't over-write /* nfs4_copy_delegation_stateid() didn't over-write
* dst, so it still has the lock stateid which we now * dst, so it still has the lock stateid which we now
......
...@@ -108,6 +108,7 @@ struct gss_auth { ...@@ -108,6 +108,7 @@ struct gss_auth {
static DEFINE_SPINLOCK(pipe_version_lock); static DEFINE_SPINLOCK(pipe_version_lock);
static struct rpc_wait_queue pipe_version_rpc_waitqueue; static struct rpc_wait_queue pipe_version_rpc_waitqueue;
static DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue); static DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue);
static void gss_put_auth(struct gss_auth *gss_auth);
static void gss_free_ctx(struct gss_cl_ctx *); static void gss_free_ctx(struct gss_cl_ctx *);
static const struct rpc_pipe_ops gss_upcall_ops_v0; static const struct rpc_pipe_ops gss_upcall_ops_v0;
...@@ -320,6 +321,7 @@ gss_release_msg(struct gss_upcall_msg *gss_msg) ...@@ -320,6 +321,7 @@ gss_release_msg(struct gss_upcall_msg *gss_msg)
if (gss_msg->ctx != NULL) if (gss_msg->ctx != NULL)
gss_put_ctx(gss_msg->ctx); gss_put_ctx(gss_msg->ctx);
rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue); rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue);
gss_put_auth(gss_msg->auth);
kfree(gss_msg); kfree(gss_msg);
} }
...@@ -498,9 +500,12 @@ gss_alloc_msg(struct gss_auth *gss_auth, ...@@ -498,9 +500,12 @@ gss_alloc_msg(struct gss_auth *gss_auth,
default: default:
err = gss_encode_v1_msg(gss_msg, service_name, gss_auth->target_name); err = gss_encode_v1_msg(gss_msg, service_name, gss_auth->target_name);
if (err) if (err)
goto err_free_msg; goto err_put_pipe_version;
}; };
kref_get(&gss_auth->kref);
return gss_msg; return gss_msg;
err_put_pipe_version:
put_pipe_version(gss_auth->net);
err_free_msg: err_free_msg:
kfree(gss_msg); kfree(gss_msg);
err: err:
...@@ -991,6 +996,8 @@ gss_create_new(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) ...@@ -991,6 +996,8 @@ gss_create_new(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor); gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);
if (gss_auth->service == 0) if (gss_auth->service == 0)
goto err_put_mech; goto err_put_mech;
if (!gssd_running(gss_auth->net))
goto err_put_mech;
auth = &gss_auth->rpc_auth; auth = &gss_auth->rpc_auth;
auth->au_cslack = GSS_CRED_SLACK >> 2; auth->au_cslack = GSS_CRED_SLACK >> 2;
auth->au_rslack = GSS_VERF_SLACK >> 2; auth->au_rslack = GSS_VERF_SLACK >> 2;
...@@ -1061,6 +1068,12 @@ gss_free_callback(struct kref *kref) ...@@ -1061,6 +1068,12 @@ gss_free_callback(struct kref *kref)
gss_free(gss_auth); gss_free(gss_auth);
} }
static void
gss_put_auth(struct gss_auth *gss_auth)
{
kref_put(&gss_auth->kref, gss_free_callback);
}
static void static void
gss_destroy(struct rpc_auth *auth) gss_destroy(struct rpc_auth *auth)
{ {
...@@ -1082,7 +1095,7 @@ gss_destroy(struct rpc_auth *auth) ...@@ -1082,7 +1095,7 @@ gss_destroy(struct rpc_auth *auth)
gss_auth->gss_pipe[1] = NULL; gss_auth->gss_pipe[1] = NULL;
rpcauth_destroy_credcache(auth); rpcauth_destroy_credcache(auth);
kref_put(&gss_auth->kref, gss_free_callback); gss_put_auth(gss_auth);
} }
/* /*
...@@ -1253,7 +1266,7 @@ gss_destroy_nullcred(struct rpc_cred *cred) ...@@ -1253,7 +1266,7 @@ gss_destroy_nullcred(struct rpc_cred *cred)
call_rcu(&cred->cr_rcu, gss_free_cred_callback); call_rcu(&cred->cr_rcu, gss_free_cred_callback);
if (ctx) if (ctx)
gss_put_ctx(ctx); gss_put_ctx(ctx);
kref_put(&gss_auth->kref, gss_free_callback); gss_put_auth(gss_auth);
} }
static void static void
......
...@@ -64,7 +64,6 @@ static void xprt_free_allocation(struct rpc_rqst *req) ...@@ -64,7 +64,6 @@ static void xprt_free_allocation(struct rpc_rqst *req)
free_page((unsigned long)xbufp->head[0].iov_base); free_page((unsigned long)xbufp->head[0].iov_base);
xbufp = &req->rq_snd_buf; xbufp = &req->rq_snd_buf;
free_page((unsigned long)xbufp->head[0].iov_base); free_page((unsigned long)xbufp->head[0].iov_base);
list_del(&req->rq_bc_pa_list);
kfree(req); kfree(req);
} }
...@@ -168,8 +167,10 @@ int xprt_setup_backchannel(struct rpc_xprt *xprt, unsigned int min_reqs) ...@@ -168,8 +167,10 @@ int xprt_setup_backchannel(struct rpc_xprt *xprt, unsigned int min_reqs)
/* /*
* Memory allocation failed, free the temporary list * Memory allocation failed, free the temporary list
*/ */
list_for_each_entry_safe(req, tmp, &tmp_list, rq_bc_pa_list) list_for_each_entry_safe(req, tmp, &tmp_list, rq_bc_pa_list) {
list_del(&req->rq_bc_pa_list);
xprt_free_allocation(req); xprt_free_allocation(req);
}
dprintk("RPC: setup backchannel transport failed\n"); dprintk("RPC: setup backchannel transport failed\n");
return -ENOMEM; return -ENOMEM;
...@@ -198,6 +199,7 @@ void xprt_destroy_backchannel(struct rpc_xprt *xprt, unsigned int max_reqs) ...@@ -198,6 +199,7 @@ void xprt_destroy_backchannel(struct rpc_xprt *xprt, unsigned int max_reqs)
xprt_dec_alloc_count(xprt, max_reqs); xprt_dec_alloc_count(xprt, max_reqs);
list_for_each_entry_safe(req, tmp, &xprt->bc_pa_list, rq_bc_pa_list) { list_for_each_entry_safe(req, tmp, &xprt->bc_pa_list, rq_bc_pa_list) {
dprintk("RPC: req=%p\n", req); dprintk("RPC: req=%p\n", req);
list_del(&req->rq_bc_pa_list);
xprt_free_allocation(req); xprt_free_allocation(req);
if (--max_reqs == 0) if (--max_reqs == 0)
break; break;
......
...@@ -510,6 +510,7 @@ static int xs_nospace(struct rpc_task *task) ...@@ -510,6 +510,7 @@ static int xs_nospace(struct rpc_task *task)
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
struct rpc_xprt *xprt = req->rq_xprt; struct rpc_xprt *xprt = req->rq_xprt;
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
struct sock *sk = transport->inet;
int ret = -EAGAIN; int ret = -EAGAIN;
dprintk("RPC: %5u xmit incomplete (%u left of %u)\n", dprintk("RPC: %5u xmit incomplete (%u left of %u)\n",
...@@ -527,7 +528,7 @@ static int xs_nospace(struct rpc_task *task) ...@@ -527,7 +528,7 @@ static int xs_nospace(struct rpc_task *task)
* window size * window size
*/ */
set_bit(SOCK_NOSPACE, &transport->sock->flags); set_bit(SOCK_NOSPACE, &transport->sock->flags);
transport->inet->sk_write_pending++; sk->sk_write_pending++;
/* ...and wait for more buffer space */ /* ...and wait for more buffer space */
xprt_wait_for_buffer_space(task, xs_nospace_callback); xprt_wait_for_buffer_space(task, xs_nospace_callback);
} }
...@@ -537,6 +538,9 @@ static int xs_nospace(struct rpc_task *task) ...@@ -537,6 +538,9 @@ static int xs_nospace(struct rpc_task *task)
} }
spin_unlock_bh(&xprt->transport_lock); spin_unlock_bh(&xprt->transport_lock);
/* Race breaker in case memory is freed before above code is called */
sk->sk_write_space(sk);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment