230 lines
8.4 KiB
Diff
230 lines
8.4 KiB
Diff
From 321f89f5812405fd3018b162535ae29bda669909 Mon Sep 17 00:00:00 2001
|
|
From: Vasily Averin <vvs@virtuozzo.com>
|
|
Date: Mon, 24 Dec 2018 14:44:42 +0300
|
|
Subject: [PATCH 1/2] sunrpc: use SVC_NET() in svcauth_gss_* functions
|
|
|
|
Signed-off-by: Vasily Averin <vvs@virtuozzo.com>
|
|
Cc: stable@vger.kernel.org
|
|
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
|
|
Signed-off-by: Jeremy Cline <jcline@redhat.com>
|
|
---
|
|
net/sunrpc/auth_gss/svcauth_gss.c | 8 ++++----
|
|
1 file changed, 4 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
|
|
index 860f2a1bbb67..1a65f88d021a 100644
|
|
--- a/net/sunrpc/auth_gss/svcauth_gss.c
|
|
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
|
|
@@ -1122,7 +1122,7 @@ static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
|
|
struct kvec *resv = &rqstp->rq_res.head[0];
|
|
struct rsi *rsip, rsikey;
|
|
int ret;
|
|
- struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);
|
|
+ struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
|
|
|
|
memset(&rsikey, 0, sizeof(rsikey));
|
|
ret = gss_read_verf(gc, argv, authp,
|
|
@@ -1233,7 +1233,7 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
|
|
uint64_t handle;
|
|
int status;
|
|
int ret;
|
|
- struct net *net = rqstp->rq_xprt->xpt_net;
|
|
+ struct net *net = SVC_NET(rqstp);
|
|
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
|
|
|
|
memset(&ud, 0, sizeof(ud));
|
|
@@ -1424,7 +1424,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
|
|
__be32 *rpcstart;
|
|
__be32 *reject_stat = resv->iov_base + resv->iov_len;
|
|
int ret;
|
|
- struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);
|
|
+ struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
|
|
|
|
dprintk("RPC: svcauth_gss: argv->iov_len = %zd\n",
|
|
argv->iov_len);
|
|
@@ -1714,7 +1714,7 @@ svcauth_gss_release(struct svc_rqst *rqstp)
|
|
struct rpc_gss_wire_cred *gc = &gsd->clcred;
|
|
struct xdr_buf *resbuf = &rqstp->rq_res;
|
|
int stat = -EINVAL;
|
|
- struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id);
|
|
+ struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
|
|
|
|
if (gc->gc_proc != RPC_GSS_PROC_DATA)
|
|
goto out;
|
|
--
|
|
2.20.1
|
|
|
|
From b7cde4db1cb620368534aaf89c82e3ee10ef4d72 Mon Sep 17 00:00:00 2001
|
|
From: Vasily Averin <vvs@virtuozzo.com>
|
|
Date: Mon, 24 Dec 2018 14:44:52 +0300
|
|
Subject: [PATCH 2/2] sunrpc: use-after-free in svc_process_common()
|
|
|
|
if node have NFSv41+ mounts inside several net namespaces
|
|
it can lead to use-after-free in svc_process_common()
|
|
|
|
svc_process_common()
|
|
/* Setup reply header */
|
|
rqstp->rq_xprt->xpt_ops->xpo_prep_reply_hdr(rqstp); <<< HERE
|
|
|
|
svc_process_common() can use incorrect rqstp->rq_xprt,
|
|
its caller function bc_svc_process() takes it from serv->sv_bc_xprt.
|
|
The problem is that serv is global structure but sv_bc_xprt
|
|
is assigned per-netnamespace.
|
|
|
|
According to Trond, the whole "let's set up rqstp->rq_xprt
|
|
for the back channel" is nothing but a giant hack in order
|
|
to work around the fact that svc_process_common() uses it
|
|
to find the xpt_ops, and perform a couple of (meaningless
|
|
for the back channel) tests of xpt_flags.
|
|
|
|
All we really need in svc_process_common() is to be able to run
|
|
rqstp->rq_xprt->xpt_ops->xpo_prep_reply_hdr()
|
|
|
|
Bruce J Fields points that this xpo_prep_reply_hdr() call
|
|
is an awfully roundabout way just to do "svc_putnl(resv, 0);"
|
|
in the tcp case.
|
|
|
|
This patch does not initialiuze rqstp->rq_xprt in bc_svc_process(),
|
|
now it calls svc_process_common() with rqstp->rq_xprt = NULL.
|
|
|
|
To adjust reply header svc_process_common() just check
|
|
rqstp->rq_prot and calls svc_tcp_prep_reply_hdr() for tcp case.
|
|
|
|
To handle rqstp->rq_xprt = NULL case in functions called from
|
|
svc_process_common() patch intruduces net namespace pointer
|
|
svc_rqst->rq_bc_net and adjust SVC_NET() definition.
|
|
Some other function was also adopted to properly handle described case.
|
|
|
|
Signed-off-by: Vasily Averin <vvs@virtuozzo.com>
|
|
Cc: stable@vger.kernel.org
|
|
Fixes: 23c20ecd4475 ("NFS: callback up - users counting cleanup")
|
|
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
|
|
Signed-off-by: Jeremy Cline <jcline@redhat.com>
|
|
---
|
|
include/linux/sunrpc/svc.h | 5 ++++-
|
|
include/trace/events/sunrpc.h | 6 ++++--
|
|
net/sunrpc/svc.c | 9 +++++----
|
|
net/sunrpc/svc_xprt.c | 5 +++--
|
|
net/sunrpc/svcsock.c | 2 +-
|
|
5 files changed, 17 insertions(+), 10 deletions(-)
|
|
|
|
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
|
|
index 73e130a840ce..fdb6b317d974 100644
|
|
--- a/include/linux/sunrpc/svc.h
|
|
+++ b/include/linux/sunrpc/svc.h
|
|
@@ -295,9 +295,12 @@ struct svc_rqst {
|
|
struct svc_cacherep * rq_cacherep; /* cache info */
|
|
struct task_struct *rq_task; /* service thread */
|
|
spinlock_t rq_lock; /* per-request lock */
|
|
+ struct net *rq_bc_net; /* pointer to backchannel's
|
|
+ * net namespace
|
|
+ */
|
|
};
|
|
|
|
-#define SVC_NET(svc_rqst) (svc_rqst->rq_xprt->xpt_net)
|
|
+#define SVC_NET(rqst) (rqst->rq_xprt ? rqst->rq_xprt->xpt_net : rqst->rq_bc_net)
|
|
|
|
/*
|
|
* Rigorous type checking on sockaddr type conversions
|
|
diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h
|
|
index bbb08a3ef5cc..a2644c494a9c 100644
|
|
--- a/include/trace/events/sunrpc.h
|
|
+++ b/include/trace/events/sunrpc.h
|
|
@@ -582,7 +582,8 @@ TRACE_EVENT(svc_process,
|
|
__field(u32, vers)
|
|
__field(u32, proc)
|
|
__string(service, name)
|
|
- __string(addr, rqst->rq_xprt->xpt_remotebuf)
|
|
+ __string(addr, rqst->rq_xprt ?
|
|
+ rqst->rq_xprt->xpt_remotebuf : "(null)")
|
|
),
|
|
|
|
TP_fast_assign(
|
|
@@ -590,7 +591,8 @@ TRACE_EVENT(svc_process,
|
|
__entry->vers = rqst->rq_vers;
|
|
__entry->proc = rqst->rq_proc;
|
|
__assign_str(service, name);
|
|
- __assign_str(addr, rqst->rq_xprt->xpt_remotebuf);
|
|
+ __assign_str(addr, rqst->rq_xprt ?
|
|
+ rqst->rq_xprt->xpt_remotebuf : "(null)");
|
|
),
|
|
|
|
TP_printk("addr=%s xid=0x%08x service=%s vers=%u proc=%u",
|
|
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
|
|
index d13e05f1a990..fb647bc01fc5 100644
|
|
--- a/net/sunrpc/svc.c
|
|
+++ b/net/sunrpc/svc.c
|
|
@@ -1172,7 +1172,8 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
|
|
clear_bit(RQ_DROPME, &rqstp->rq_flags);
|
|
|
|
/* Setup reply header */
|
|
- rqstp->rq_xprt->xpt_ops->xpo_prep_reply_hdr(rqstp);
|
|
+ if (rqstp->rq_prot == IPPROTO_TCP)
|
|
+ svc_tcp_prep_reply_hdr(rqstp);
|
|
|
|
svc_putu32(resv, rqstp->rq_xid);
|
|
|
|
@@ -1244,7 +1245,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
|
|
* for lower versions. RPC_PROG_MISMATCH seems to be the closest
|
|
* fit.
|
|
*/
|
|
- if (versp->vs_need_cong_ctrl &&
|
|
+ if (versp->vs_need_cong_ctrl && rqstp->rq_xprt &&
|
|
!test_bit(XPT_CONG_CTRL, &rqstp->rq_xprt->xpt_flags))
|
|
goto err_bad_vers;
|
|
|
|
@@ -1336,7 +1337,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
|
|
return 0;
|
|
|
|
close:
|
|
- if (test_bit(XPT_TEMP, &rqstp->rq_xprt->xpt_flags))
|
|
+ if (rqstp->rq_xprt && test_bit(XPT_TEMP, &rqstp->rq_xprt->xpt_flags))
|
|
svc_close_xprt(rqstp->rq_xprt);
|
|
dprintk("svc: svc_process close\n");
|
|
return 0;
|
|
@@ -1459,10 +1460,10 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
|
|
dprintk("svc: %s(%p)\n", __func__, req);
|
|
|
|
/* Build the svc_rqst used by the common processing routine */
|
|
- rqstp->rq_xprt = serv->sv_bc_xprt;
|
|
rqstp->rq_xid = req->rq_xid;
|
|
rqstp->rq_prot = req->rq_xprt->prot;
|
|
rqstp->rq_server = serv;
|
|
+ rqstp->rq_bc_net = req->rq_xprt->xprt_net;
|
|
|
|
rqstp->rq_addrlen = sizeof(req->rq_xprt->addr);
|
|
memcpy(&rqstp->rq_addr, &req->rq_xprt->addr, rqstp->rq_addrlen);
|
|
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
|
|
index 83ccd0221c98..6cf0fd37cbf0 100644
|
|
--- a/net/sunrpc/svc_xprt.c
|
|
+++ b/net/sunrpc/svc_xprt.c
|
|
@@ -469,10 +469,11 @@ static struct svc_xprt *svc_xprt_dequeue(struct svc_pool *pool)
|
|
*/
|
|
void svc_reserve(struct svc_rqst *rqstp, int space)
|
|
{
|
|
+ struct svc_xprt *xprt = rqstp->rq_xprt;
|
|
+
|
|
space += rqstp->rq_res.head[0].iov_len;
|
|
|
|
- if (space < rqstp->rq_reserved) {
|
|
- struct svc_xprt *xprt = rqstp->rq_xprt;
|
|
+ if (xprt && space < rqstp->rq_reserved) {
|
|
atomic_sub((rqstp->rq_reserved - space), &xprt->xpt_reserved);
|
|
rqstp->rq_reserved = space;
|
|
|
|
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
|
|
index 5445145e639c..0221f565538e 100644
|
|
--- a/net/sunrpc/svcsock.c
|
|
+++ b/net/sunrpc/svcsock.c
|
|
@@ -1198,7 +1198,7 @@ static int svc_tcp_sendto(struct svc_rqst *rqstp)
|
|
/*
|
|
* Setup response header. TCP has a 4B record length field.
|
|
*/
|
|
-static void svc_tcp_prep_reply_hdr(struct svc_rqst *rqstp)
|
|
+void svc_tcp_prep_reply_hdr(struct svc_rqst *rqstp)
|
|
{
|
|
struct kvec *resv = &rqstp->rq_res.head[0];
|
|
|
|
--
|
|
2.20.1
|
|
|