- Address several use-after-free and memory leak bugs
 
 - Prevent a backchannel livelock
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJemdwYAAoJEDNqszNvZn+XU1oQAKOm9vypO6w252kXdhFSxAlB
 3tMxXALNDrFP3PXsKCa/sKKMRvkUkx+9pdnTuXDPvffd3ZgyB8DzJilryEtiqT4Y
 JsuoWHg2QyNeKUFGmtZ5AsefPaR8WL/aiYPTi1PUqnq4rNPjAgOGgLUv+LME2jFU
 Yx773d5CNHXDq6zv1Au0128URnQZDy/7URdfgX1FhLA8aQWjiG08fhBEGncXjV/X
 mo3RMCwE2uzNRruW7OJyCehb8d+IKBDZ0LEeZDW/ve4hNtL+Ke5eCEoemYtUN07e
 U3gRMB8Pt+55L+ZFP8KJYOtfRx2SkOTMcbASC2z/WECq5vumGmn4WovSSVJFGIUN
 5WVf8ADM2w3RmTFh11Jl5mZnziGRNY/4hAW7PrR4ZDhJxjdKA+iLLd7571kkCE63
 II6qxw/WV7Yz3T6v4BoOcDf1DOylnS1JXqmPGYia2aAhyFZgRVasOVIkB0meaaFe
 zSKzKsTrir1Ru8/xt5zIgyEQwqATp2rwzkoPuTeQZLOht0fsSIGBpD1ZWXUaMAji
 cfojhd4731cvoxMMGG27IMiHTG6rpKneaZ21Z/7/61P+cjHm/ITOLZzzRvhQMQU7
 wuskRf3KTs+3k4x6P9E0qQU1DcJkPSYGq+JDdh389Plald4MLTAZYjIK+J3X35oL
 QNnUeKzr1YhWWqgchthG
 =Zoup
 -----END PGP SIGNATURE-----

Merge tag 'nfsd-5.7-rc-1' of git://git.linux-nfs.org/projects/cel/cel-2.6

Pull nfsd fixes from Chuck Lever:
 "The first set of 5.7-rc fixes for NFS server issues.

  These were all unresolved at the time the 5.7 window opened, and
  needed some additional time to ensure they were correctly addressed.
  They are ready now.

  At the moment I know of one more urgent issue regarding the NFS
  server. A fix has been tested and is under review. I expect to send
  one more pull request, containing this fix (which now consists of 3
  patches).

  Fixes:

   - Address several use-after-free and memory leak bugs

   - Prevent a backchannel livelock"

* tag 'nfsd-5.7-rc-1' of git://git.linux-nfs.org/projects/cel/cel-2.6:
  svcrdma: Fix leak of svc_rdma_recv_ctxt objects
  svcrdma: Fix trace point use-after-free race
  SUNRPC: Fix backchannel RPC soft lockups
  SUNRPC/cache: Fix unsafe traverse caused double-free in cache_purge
  nfsd: memory corruption in nfsd4_lock()
This commit is contained in:
Linus Torvalds 2020-04-23 09:33:43 -07:00
commit 1ddd873948
13 changed files with 89 additions and 44 deletions

View File

@ -1312,6 +1312,7 @@ nfsd4_run_cb_work(struct work_struct *work)
container_of(work, struct nfsd4_callback, cb_work); container_of(work, struct nfsd4_callback, cb_work);
struct nfs4_client *clp = cb->cb_clp; struct nfs4_client *clp = cb->cb_clp;
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
int flags;
if (cb->cb_need_restart) { if (cb->cb_need_restart) {
cb->cb_need_restart = false; cb->cb_need_restart = false;
@ -1340,7 +1341,8 @@ nfsd4_run_cb_work(struct work_struct *work)
} }
cb->cb_msg.rpc_cred = clp->cl_cb_cred; cb->cb_msg.rpc_cred = clp->cl_cb_cred;
rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN, flags = clp->cl_minorversion ? RPC_TASK_NOCONNECT : RPC_TASK_SOFTCONN;
rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | flags,
cb->cb_ops ? &nfsd4_cb_ops : &nfsd4_cb_probe_ops, cb); cb->cb_ops ? &nfsd4_cb_ops : &nfsd4_cb_probe_ops, cb);
} }

View File

@ -267,6 +267,8 @@ find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
if (!nbl) { if (!nbl) {
nbl= kmalloc(sizeof(*nbl), GFP_KERNEL); nbl= kmalloc(sizeof(*nbl), GFP_KERNEL);
if (nbl) { if (nbl) {
INIT_LIST_HEAD(&nbl->nbl_list);
INIT_LIST_HEAD(&nbl->nbl_lru);
fh_copy_shallow(&nbl->nbl_fh, fh); fh_copy_shallow(&nbl->nbl_fh, fh);
locks_init_lock(&nbl->nbl_lock); locks_init_lock(&nbl->nbl_lock);
nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client, nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,

View File

@ -170,6 +170,7 @@ extern bool svc_rdma_post_recvs(struct svcxprt_rdma *rdma);
extern void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, extern void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma,
struct svc_rdma_recv_ctxt *ctxt); struct svc_rdma_recv_ctxt *ctxt);
extern void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma); extern void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma);
extern void svc_rdma_release_rqst(struct svc_rqst *rqstp);
extern int svc_rdma_recvfrom(struct svc_rqst *); extern int svc_rdma_recvfrom(struct svc_rqst *);
/* svc_rdma_rw.c */ /* svc_rdma_rw.c */

View File

@ -1695,17 +1695,15 @@ DECLARE_EVENT_CLASS(svcrdma_sendcomp_event,
TRACE_EVENT(svcrdma_post_send, TRACE_EVENT(svcrdma_post_send,
TP_PROTO( TP_PROTO(
const struct ib_send_wr *wr, const struct ib_send_wr *wr
int status
), ),
TP_ARGS(wr, status), TP_ARGS(wr),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const void *, cqe) __field(const void *, cqe)
__field(unsigned int, num_sge) __field(unsigned int, num_sge)
__field(u32, inv_rkey) __field(u32, inv_rkey)
__field(int, status)
), ),
TP_fast_assign( TP_fast_assign(
@ -1713,12 +1711,11 @@ TRACE_EVENT(svcrdma_post_send,
__entry->num_sge = wr->num_sge; __entry->num_sge = wr->num_sge;
__entry->inv_rkey = (wr->opcode == IB_WR_SEND_WITH_INV) ? __entry->inv_rkey = (wr->opcode == IB_WR_SEND_WITH_INV) ?
wr->ex.invalidate_rkey : 0; wr->ex.invalidate_rkey : 0;
__entry->status = status;
), ),
TP_printk("cqe=%p num_sge=%u inv_rkey=0x%08x status=%d", TP_printk("cqe=%p num_sge=%u inv_rkey=0x%08x",
__entry->cqe, __entry->num_sge, __entry->cqe, __entry->num_sge,
__entry->inv_rkey, __entry->status __entry->inv_rkey
) )
); );
@ -1783,26 +1780,23 @@ TRACE_EVENT(svcrdma_wc_receive,
TRACE_EVENT(svcrdma_post_rw, TRACE_EVENT(svcrdma_post_rw,
TP_PROTO( TP_PROTO(
const void *cqe, const void *cqe,
int sqecount, int sqecount
int status
), ),
TP_ARGS(cqe, sqecount, status), TP_ARGS(cqe, sqecount),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(const void *, cqe) __field(const void *, cqe)
__field(int, sqecount) __field(int, sqecount)
__field(int, status)
), ),
TP_fast_assign( TP_fast_assign(
__entry->cqe = cqe; __entry->cqe = cqe;
__entry->sqecount = sqecount; __entry->sqecount = sqecount;
__entry->status = status;
), ),
TP_printk("cqe=%p sqecount=%d status=%d", TP_printk("cqe=%p sqecount=%d",
__entry->cqe, __entry->sqecount, __entry->status __entry->cqe, __entry->sqecount
) )
); );
@ -1870,6 +1864,34 @@ DECLARE_EVENT_CLASS(svcrdma_sendqueue_event,
DEFINE_SQ_EVENT(full); DEFINE_SQ_EVENT(full);
DEFINE_SQ_EVENT(retry); DEFINE_SQ_EVENT(retry);
TRACE_EVENT(svcrdma_sq_post_err,
TP_PROTO(
const struct svcxprt_rdma *rdma,
int status
),
TP_ARGS(rdma, status),
TP_STRUCT__entry(
__field(int, avail)
__field(int, depth)
__field(int, status)
__string(addr, rdma->sc_xprt.xpt_remotebuf)
),
TP_fast_assign(
__entry->avail = atomic_read(&rdma->sc_sq_avail);
__entry->depth = rdma->sc_sq_depth;
__entry->status = status;
__assign_str(addr, rdma->sc_xprt.xpt_remotebuf);
),
TP_printk("addr=%s sc_sq_avail=%d/%d status=%d",
__get_str(addr), __entry->avail, __entry->depth,
__entry->status
)
);
#endif /* _TRACE_RPCRDMA_H */ #endif /* _TRACE_RPCRDMA_H */
#include <trace/define_trace.h> #include <trace/define_trace.h>

View File

@ -529,7 +529,6 @@ void cache_purge(struct cache_detail *detail)
{ {
struct cache_head *ch = NULL; struct cache_head *ch = NULL;
struct hlist_head *head = NULL; struct hlist_head *head = NULL;
struct hlist_node *tmp = NULL;
int i = 0; int i = 0;
spin_lock(&detail->hash_lock); spin_lock(&detail->hash_lock);
@ -541,7 +540,9 @@ void cache_purge(struct cache_detail *detail)
dprintk("RPC: %d entries in %s cache\n", detail->entries, detail->name); dprintk("RPC: %d entries in %s cache\n", detail->entries, detail->name);
for (i = 0; i < detail->hash_size; i++) { for (i = 0; i < detail->hash_size; i++) {
head = &detail->hash_table[i]; head = &detail->hash_table[i];
hlist_for_each_entry_safe(ch, tmp, head, cache_list) { while (!hlist_empty(head)) {
ch = hlist_entry(head->first, struct cache_head,
cache_list);
sunrpc_begin_cache_remove_entry(ch, detail); sunrpc_begin_cache_remove_entry(ch, detail);
spin_unlock(&detail->hash_lock); spin_unlock(&detail->hash_lock);
sunrpc_end_cache_remove_entry(ch, detail); sunrpc_end_cache_remove_entry(ch, detail);

View File

@ -908,9 +908,6 @@ int svc_send(struct svc_rqst *rqstp)
if (!xprt) if (!xprt)
goto out; goto out;
/* release the receive skb before sending the reply */
xprt->xpt_ops->xpo_release_rqst(rqstp);
/* calculate over-all length */ /* calculate over-all length */
xb = &rqstp->rq_res; xb = &rqstp->rq_res;
xb->len = xb->head[0].iov_len + xb->len = xb->head[0].iov_len +
@ -1040,6 +1037,8 @@ static void svc_delete_xprt(struct svc_xprt *xprt)
dprintk("svc: svc_delete_xprt(%p)\n", xprt); dprintk("svc: svc_delete_xprt(%p)\n", xprt);
xprt->xpt_ops->xpo_detach(xprt); xprt->xpt_ops->xpo_detach(xprt);
if (xprt->xpt_bc_xprt)
xprt->xpt_bc_xprt->ops->close(xprt->xpt_bc_xprt);
spin_lock_bh(&serv->sv_lock); spin_lock_bh(&serv->sv_lock);
list_del_init(&xprt->xpt_list); list_del_init(&xprt->xpt_list);

View File

@ -527,6 +527,8 @@ static int svc_udp_sendto(struct svc_rqst *rqstp)
unsigned int uninitialized_var(sent); unsigned int uninitialized_var(sent);
int err; int err;
svc_release_udp_skb(rqstp);
svc_set_cmsg_data(rqstp, cmh); svc_set_cmsg_data(rqstp, cmh);
err = xprt_sock_sendmsg(svsk->sk_sock, &msg, xdr, 0, 0, &sent); err = xprt_sock_sendmsg(svsk->sk_sock, &msg, xdr, 0, 0, &sent);
@ -1076,6 +1078,8 @@ static int svc_tcp_sendto(struct svc_rqst *rqstp)
unsigned int uninitialized_var(sent); unsigned int uninitialized_var(sent);
int err; int err;
svc_release_skb(rqstp);
err = xprt_sock_sendmsg(svsk->sk_sock, &msg, xdr, 0, marker, &sent); err = xprt_sock_sendmsg(svsk->sk_sock, &msg, xdr, 0, marker, &sent);
xdr_free_bvec(xdr); xdr_free_bvec(xdr);
if (err < 0 || sent != (xdr->len + sizeof(marker))) if (err < 0 || sent != (xdr->len + sizeof(marker)))

View File

@ -244,6 +244,8 @@ static void
xprt_rdma_bc_close(struct rpc_xprt *xprt) xprt_rdma_bc_close(struct rpc_xprt *xprt)
{ {
dprintk("svcrdma: %s: xprt %p\n", __func__, xprt); dprintk("svcrdma: %s: xprt %p\n", __func__, xprt);
xprt_disconnect_done(xprt);
xprt->cwnd = RPC_CWNDSHIFT; xprt->cwnd = RPC_CWNDSHIFT;
} }

View File

@ -223,6 +223,26 @@ void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma,
svc_rdma_recv_ctxt_destroy(rdma, ctxt); svc_rdma_recv_ctxt_destroy(rdma, ctxt);
} }
/**
* svc_rdma_release_rqst - Release transport-specific per-rqst resources
* @rqstp: svc_rqst being released
*
* Ensure that the recv_ctxt is released whether or not a Reply
* was sent. For example, the client could close the connection,
* or svc_process could drop an RPC, before the Reply is sent.
*/
void svc_rdma_release_rqst(struct svc_rqst *rqstp)
{
struct svc_rdma_recv_ctxt *ctxt = rqstp->rq_xprt_ctxt;
struct svc_xprt *xprt = rqstp->rq_xprt;
struct svcxprt_rdma *rdma =
container_of(xprt, struct svcxprt_rdma, sc_xprt);
rqstp->rq_xprt_ctxt = NULL;
if (ctxt)
svc_rdma_recv_ctxt_put(rdma, ctxt);
}
static int __svc_rdma_post_recv(struct svcxprt_rdma *rdma, static int __svc_rdma_post_recv(struct svcxprt_rdma *rdma,
struct svc_rdma_recv_ctxt *ctxt) struct svc_rdma_recv_ctxt *ctxt)
{ {
@ -820,6 +840,8 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
__be32 *p; __be32 *p;
int ret; int ret;
rqstp->rq_xprt_ctxt = NULL;
spin_lock(&rdma_xprt->sc_rq_dto_lock); spin_lock(&rdma_xprt->sc_rq_dto_lock);
ctxt = svc_rdma_next_recv_ctxt(&rdma_xprt->sc_read_complete_q); ctxt = svc_rdma_next_recv_ctxt(&rdma_xprt->sc_read_complete_q);
if (ctxt) { if (ctxt) {

View File

@ -323,8 +323,6 @@ static int svc_rdma_post_chunk_ctxt(struct svc_rdma_chunk_ctxt *cc)
if (atomic_sub_return(cc->cc_sqecount, if (atomic_sub_return(cc->cc_sqecount,
&rdma->sc_sq_avail) > 0) { &rdma->sc_sq_avail) > 0) {
ret = ib_post_send(rdma->sc_qp, first_wr, &bad_wr); ret = ib_post_send(rdma->sc_qp, first_wr, &bad_wr);
trace_svcrdma_post_rw(&cc->cc_cqe,
cc->cc_sqecount, ret);
if (ret) if (ret)
break; break;
return 0; return 0;
@ -337,6 +335,7 @@ static int svc_rdma_post_chunk_ctxt(struct svc_rdma_chunk_ctxt *cc)
trace_svcrdma_sq_retry(rdma); trace_svcrdma_sq_retry(rdma);
} while (1); } while (1);
trace_svcrdma_sq_post_err(rdma, ret);
set_bit(XPT_CLOSE, &xprt->xpt_flags); set_bit(XPT_CLOSE, &xprt->xpt_flags);
/* If even one was posted, there will be a completion. */ /* If even one was posted, there will be a completion. */

View File

@ -322,15 +322,17 @@ int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr)
} }
svc_xprt_get(&rdma->sc_xprt); svc_xprt_get(&rdma->sc_xprt);
trace_svcrdma_post_send(wr);
ret = ib_post_send(rdma->sc_qp, wr, NULL); ret = ib_post_send(rdma->sc_qp, wr, NULL);
trace_svcrdma_post_send(wr, ret); if (ret)
if (ret) { break;
set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); return 0;
svc_xprt_put(&rdma->sc_xprt);
wake_up(&rdma->sc_send_wait);
}
break;
} }
trace_svcrdma_sq_post_err(rdma, ret);
set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags);
svc_xprt_put(&rdma->sc_xprt);
wake_up(&rdma->sc_send_wait);
return ret; return ret;
} }
@ -924,12 +926,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
ret = svc_rdma_send_reply_msg(rdma, sctxt, rctxt, rqstp); ret = svc_rdma_send_reply_msg(rdma, sctxt, rctxt, rqstp);
if (ret < 0) if (ret < 0)
goto err1; goto err1;
ret = 0; return 0;
out:
rqstp->rq_xprt_ctxt = NULL;
svc_rdma_recv_ctxt_put(rdma, rctxt);
return ret;
err2: err2:
if (ret != -E2BIG && ret != -EINVAL) if (ret != -E2BIG && ret != -EINVAL)
@ -938,16 +935,14 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
ret = svc_rdma_send_error_msg(rdma, sctxt, rqstp); ret = svc_rdma_send_error_msg(rdma, sctxt, rqstp);
if (ret < 0) if (ret < 0)
goto err1; goto err1;
ret = 0; return 0;
goto out;
err1: err1:
svc_rdma_send_ctxt_put(rdma, sctxt); svc_rdma_send_ctxt_put(rdma, sctxt);
err0: err0:
trace_svcrdma_send_failed(rqstp, ret); trace_svcrdma_send_failed(rqstp, ret);
set_bit(XPT_CLOSE, &xprt->xpt_flags); set_bit(XPT_CLOSE, &xprt->xpt_flags);
ret = -ENOTCONN; return -ENOTCONN;
goto out;
} }
/** /**

View File

@ -71,7 +71,6 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
struct sockaddr *sa, int salen, struct sockaddr *sa, int salen,
int flags); int flags);
static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt); static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt);
static void svc_rdma_release_rqst(struct svc_rqst *);
static void svc_rdma_detach(struct svc_xprt *xprt); static void svc_rdma_detach(struct svc_xprt *xprt);
static void svc_rdma_free(struct svc_xprt *xprt); static void svc_rdma_free(struct svc_xprt *xprt);
static int svc_rdma_has_wspace(struct svc_xprt *xprt); static int svc_rdma_has_wspace(struct svc_xprt *xprt);
@ -552,10 +551,6 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
return NULL; return NULL;
} }
static void svc_rdma_release_rqst(struct svc_rqst *rqstp)
{
}
/* /*
* When connected, an svc_xprt has at least two references: * When connected, an svc_xprt has at least two references:
* *

View File

@ -2584,6 +2584,7 @@ static int bc_send_request(struct rpc_rqst *req)
static void bc_close(struct rpc_xprt *xprt) static void bc_close(struct rpc_xprt *xprt)
{ {
xprt_disconnect_done(xprt);
} }
/* /*