rpcbind: pair all svc_getargs() calls with svc_freeargs() to avoid memory leak
Subsystems (2)
PATCH 1/1 rpcbind: pair all svc_getargs() calls with svc_freeargs() to avoid memory leak
This patch is to address CVE-2017-8779 "rpcbomb" in rpcbind, discussed at [1], [2], [3]. The last link suggests this issue is actually a bug in rpcbind, which led me here.
The leak caused by the reproducer at [4] appears to come from rpcb_service_4(), in the case where svc_getargs() returns false and the function had an early return, rather than passing through the cleanup path at done:, as would otherwise occur.
It also addresses a couple of other locations where the same fault seems to exist, though I haven't been able to exercise those. I hope someone more intimate with rpc(3) can confirm my understanding is correct, and that I haven't introduced any new bugs.
Without this patch, using the reproducer (and variants) repeatedly against rpcbind with a numBytes argument of 1_000_000_000, /proc/$(pidof rpcbind)/status reports VmSize increase of 976564 kB each call, and VmRSS increase of around 260 kB every 33 calls - the specific numbers are probably an artifact of my rhel/glibc version. With the patch, there is a small (~50 kB) VmSize increase with the first message, but thereafter both VmSize and VmRSS remain steady.
[1]: http://seclists.org/oss-sec/2017/q2/209
[2]: https://bugzilla.redhat.com/show_bug.cgi?id=1448124
[3]: https://sourceware.org/ml/libc-alpha/2017-05/msg00129.html
[4]: https://github.com/guidovranken/rpcbomb/
--- src/pmap_svc.c | 56 +++++++++++++++++++++++++++++++++++++++++++++--------- src/rpcb_svc.c | 2 +- src/rpcb_svc_4.c | 2 +- src/rpcb_svc_com.c | 8 ++++++++ 4 files changed, 57 insertions(+), 11 deletions(-)
src/pmap_svc.c +47 -9
@@ -175,6 +175,7 @@ pmapproc_change(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt, unsigned longlong ans;uid_t uid;char uidbuf[32 ]; +int rc = TRUE;/* * Can't use getpwnam here. We might end up calling ourselves
@@ -194,7 +195,8 @@ pmapproc_change(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt, unsigned longif (!svc_getargs (xprt, (xdrproc_t ) xdr_pmap, (char *)®)) {svcerr_decode (xprt); -return (FALSE); + rc = FALSE; + goto done; }#ifdef RPCBIND_DEBUG if (debugging)
@@ -205,7 +207,8 @@ pmapproc_change(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt, unsigned longif (!check_access (xprt, op, reg.pm_prog,PMAPVERS )) {svcerr_weakauth (xprt); -return (FALSE); + rc = (FALSE); + goto done; } rpcbreg.r_prog = reg.pm_prog;
@@ -258,7 +261,16 @@ done_change:rpcbs_set (RPCBVERS_2_STAT , ans); elserpcbs_unset (RPCBVERS_2_STAT ,ans ); -return (TRUE); +done: +if (!svc_freeargs (xprt, (xdrproc_t ) xdr_pmap, (char *)®)) { +if (debugging) { + (void )xlog (LOG_DEBUG ,"unable to free arguments\n" ); +if (doabort) { +rpcbind_abort (); + } + } + } +return (rc); }/* ARGSUSED */
@@ -272,15 +284,18 @@ pmapproc_getport(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt)#ifdef RPCBIND_DEBUG char *uaddr;#endif +int rc = TRUE;if (!svc_getargs (xprt, (xdrproc_t ) xdr_pmap, (char *)®)) {svcerr_decode (xprt); -return (FALSE); + rc = FALSE; + goto done; }if (!check_access (xprt,PMAPPROC_GETPORT , reg.pm_prog,PMAPVERS )) {svcerr_weakauth (xprt); -return FALSE; + rc = FALSE; + goto done; }#ifdef RPCBIND_DEBUG
@@ -330,21 +345,34 @@ pmapproc_getport(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt)pmap_ipprot2netid (reg.pm_prot) ?:"<unknown>" , port ? udptrans : ""); -return (TRUE); +done: +if (!svc_freeargs (xprt, (xdrproc_t ) xdr_pmap, (char *)®)) { +if (debugging) { + (void )xlog (LOG_DEBUG ,"unable to free arguments\n" ); +if (doabort) { +rpcbind_abort (); + } + } + } +return (rc); }/* ARGSUSED */ static bool_t pmapproc_dump(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt) { + int rc = TRUE; + if (!svc_getargs(xprt, (xdrproc_t)xdr_void, NULL)) { svcerr_decode(xprt); - return (FALSE); + rc = FALSE; + goto done; } if (!check_access(xprt, PMAPPROC_DUMP, 0, PMAPVERS)) { svcerr_weakauth(xprt); - return FALSE; + rc = FALSE; + goto done; } if ((!svc_sendreply(xprt, (xdrproc_t) xdr_pmaplist_ptr,
@@ -354,7 +382,17 @@ pmapproc_dump(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt)rpcbind_abort (); } } -return (TRUE); + +done: +if (!svc_freeargs (xprt, (xdrproc_t ) xdr_pmap, (char *)NULL )) { +if (debugging) { + (void )xlog (LOG_DEBUG ,"unable to free arguments\n" ); +if (doabort) { +rpcbind_abort (); + } + } + } +return (rc); }int pmap_netid2ipprot (const char *netid)
src/rpcb_svc.c +1 -1
@@ -166,7 +166,7 @@ rpcb_service_3(struct svc_req *rqstp, SVCXPRT *transp)svcerr_decode (transp);if (debugging) (void )xlog (LOG_DEBUG ,"rpcbind: could not decode" ); -return ; + goto done; } if (rqstp->rq_proc == RPCBPROC_SET
src/rpcb_svc_4.c +1 -1
@@ -218,7 +218,7 @@ rpcb_service_4(struct svc_req *rqstp, SVCXPRT *transp)svcerr_decode (transp);if (debugging) (void )xlog (LOG_DEBUG ,"rpcbind: could not decode\n" ); -return ; + goto done; } if (rqstp->rq_proc == RPCBPROC_SET
src/rpcb_svc_com.c +8 -0
@@ -927,6 +927,14 @@ error:if (call_msg.rm_xid !=0 ) (void )free_slot_by_xid (call_msg.rm_xid); out: +if (!svc_freeargs (transp, (xdrproc_t ) xdr_rmtcall_args, (char *) &a)) { +if (debugging) { + (void )xlog (LOG_DEBUG ,"unable to free arguments\n" ); +if (doabort) { +rpcbind_abort (); + } + } + }if (local_uaddr)free (local_uaddr);if (buf_alloc)
Signature · 5 lines
1.8.3.1 -- Doran Moppert Red Hat Product Security
Quoted 23 lines · Doran Moppert
This patch is to address CVE-2017-8779 "rpcbomb" in rpcbind, discussed at [1], [2], [3]. The last link suggests this issue is actually a bug in rpcbind, which led me here.
The leak caused by the reproducer at [4] appears to come from
rpcb_service_4(), in the case wheresvc_getargs()returns false and the function had an early return, rather than passing through the cleanup path at done:, as would otherwise occur.It also addresses a couple of other locations where the same fault seems to exist, though I haven't been able to exercise those. I hope someone more intimate with rpc(3) can confirm my understanding is correct, and that I haven't introduced any new bugs.
Without this patch, using the reproducer (and variants) repeatedly against rpcbind with a numBytes argument of 1_000_000_000, /proc/$(pidof rpcbind)/status reports VmSize increase of 976564 kB each call, and VmRSS increase of around 260 kB every 33 calls - the specific numbers are probably an artifact of my rhel/glibc version. With the patch, there is a small (~50 kB) VmSize increase with the first message, but thereafter both VmSize and VmRSS remain steady.
Quoted 7 lines
[1]: http://seclists.org/oss-sec/2017/q2/209
[2]: https://bugzilla.redhat.com/show_bug.cgi?id=1448124
[3]: https://sourceware.org/ml/libc-alpha/2017-05/msg00129.html
[4]: https://github.com/guidovranken/rpcbomb/Signed-off-by Doran Moppert <[email protected]>
Committed...
steved.
Quoted 181 lines
--- src/pmap_svc.c | 56 +++++++++++++++++++++++++++++++++++++++++++++--------- src/rpcb_svc.c | 2 +- src/rpcb_svc_4.c | 2 +- src/rpcb_svc_com.c | 8 ++++++++ 4 files changed, 57 insertions(+), 11 deletions(-)src/pmap_svc.c +47
-9@@ -175,6 +175,7 @@ pmapproc_change(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt, unsigned longlong ans;uid_t uid;char uidbuf[32 ]; +int rc = TRUE;/* * Can't use getpwnam here. We might end up calling ourselves @@ -194,7 +195,8 @@ pmapproc_change(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt, unsigned longif (!svc_getargs (xprt, (xdrproc_t ) xdr_pmap, (char *)®)) {svcerr_decode (xprt); -return (FALSE); + rc = FALSE; + goto done; }#ifdef RPCBIND_DEBUG if (debugging)@@ -205,7 +207,8 @@ pmapproc_change(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt, unsigned longif (!check_access (xprt, op, reg.pm_prog,PMAPVERS )) {svcerr_weakauth (xprt); -return (FALSE); + rc = (FALSE); + goto done; } rpcbreg.r_prog = reg.pm_prog;@@ -258,7 +261,16 @@ done_change:rpcbs_set (RPCBVERS_2_STAT , ans); elserpcbs_unset (RPCBVERS_2_STAT ,ans ); -return (TRUE); +done: +if (!svc_freeargs (xprt, (xdrproc_t ) xdr_pmap, (char *)®)) { +if (debugging) { + (void )xlog (LOG_DEBUG ,"unable to free arguments\n" ); +if (doabort) { +rpcbind_abort (); + } + } + } +return (rc); }/* ARGSUSED */ @@ -272,15 +284,18 @@ pmapproc_getport(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt)#ifdef RPCBIND_DEBUG char *uaddr;#endif +int rc = TRUE;if (!svc_getargs (xprt, (xdrproc_t ) xdr_pmap, (char *)®)) {svcerr_decode (xprt); -return (FALSE); + rc = FALSE; + goto done; }if (!check_access (xprt,PMAPPROC_GETPORT , reg.pm_prog,PMAPVERS )) {svcerr_weakauth (xprt); -return FALSE; + rc = FALSE; + goto done; }#ifdef RPCBIND_DEBUG @@ -330,21 +345,34 @@ pmapproc_getport(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt)pmap_ipprot2netid (reg.pm_prot) ?:"<unknown>" , port ? udptrans : ""); -return (TRUE); +done: +if (!svc_freeargs (xprt, (xdrproc_t ) xdr_pmap, (char *)®)) { +if (debugging) { + (void )xlog (LOG_DEBUG ,"unable to free arguments\n" ); +if (doabort) { +rpcbind_abort (); + } + } + } +return (rc); }/* ARGSUSED */ static bool_t pmapproc_dump(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt) { + int rc = TRUE; + if (!svc_getargs(xprt, (xdrproc_t)xdr_void, NULL)) { svcerr_decode(xprt); - return (FALSE); + rc = FALSE; + goto done; } if (!check_access(xprt, PMAPPROC_DUMP, 0, PMAPVERS)) { svcerr_weakauth(xprt); - return FALSE; + rc = FALSE; + goto done; } if ((!svc_sendreply(xprt, (xdrproc_t) xdr_pmaplist_ptr,@@ -354,7 +382,17 @@ pmapproc_dump(struct svc_req *rqstp /*__unused*/, SVCXPRT *xprt)rpcbind_abort (); } } -return (TRUE); + +done: +if (!svc_freeargs (xprt, (xdrproc_t ) xdr_pmap, (char *)NULL )) { +if (debugging) { + (void )xlog (LOG_DEBUG ,"unable to free arguments\n" ); +if (doabort) { +rpcbind_abort (); + } + } + } +return (rc); }int pmap_netid2ipprot (const char *netid)src/rpcb_svc.c +1
-1@@ -166,7 +166,7 @@ rpcb_service_3(struct svc_req *rqstp, SVCXPRT *transp)svcerr_decode (transp);if (debugging) (void )xlog (LOG_DEBUG ,"rpcbind: could not decode" ); -return ; + goto done; } if (rqstp->rq_proc == RPCBPROC_SETsrc/rpcb_svc_4.c +1
-1@@ -218,7 +218,7 @@ rpcb_service_4(struct svc_req *rqstp, SVCXPRT *transp)svcerr_decode (transp);if (debugging) (void )xlog (LOG_DEBUG ,"rpcbind: could not decode\n" ); -return ; + goto done; } if (rqstp->rq_proc == RPCBPROC_SETsrc/rpcb_svc_com.c +8
-0@@ -927,6 +927,14 @@ error:if (call_msg.rm_xid !=0 ) (void )free_slot_by_xid (call_msg.rm_xid); out: +if (!svc_freeargs (transp, (xdrproc_t ) xdr_rmtcall_args, (char *) &a)) { +if (debugging) { + (void )xlog (LOG_DEBUG ,"unable to free arguments\n" ); +if (doabort) { +rpcbind_abort (); + } + } + }if (local_uaddr)free (local_uaddr);if (buf_alloc)