[PATCH] DoS in RPC implementation (CVE-2011-4069)
Jeff Law
law@redhat.com
Wed Nov 28 18:17:00 GMT 2012
On 11/21/2012 03:10 PM, Roland McGrath wrote:
>> --- a/sunrpc/Versions
>> +++ b/sunrpc/Versions
>> @@ -118,5 +118,6 @@ libc {
>> }
>> GLIBC_PRIVATE {
>> __libc_clntudp_bufcreate; __libc_rpc_getport;
>> + __svc_accept_failed;
> [...]
>> --- a/sunrpc/rpc/svc.h
>> +++ b/sunrpc/rpc/svc.h
>> @@ -316,4 +316,5 @@ extern SVCXPRT *svcunix_create (int __sock, u_int __sendsize, u_int __recvsize,
>>
>> __END_DECLS
>>
>> +extern void __svc_accept_failed (void) attribute_hidden;
>
> Either it's hidden or it needs to be exported, not both.
It should be hidden (I think); it doesn't need to be used by anything
except the internal routines within the sunrpc code.
Here's the updated version with the patch to sunrpc/Versions eliminated.
-------------- next part --------------
diff --git a/ChangeLog b/ChangeLog
index 1a2e4da..3c9427f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2012-11-28 Jeff Law <law@redhat.com>
+ Martin Osvald <mosvald@redhat.com>
+
+ * sunrpc/rpc/svc.h (__svc_accept_failed): New prototype.
+ * sunrpc/svc.c: Include time.h.
+ (__svc_accept_failed): New function.
+ * sunrpc/svc_tcp.c (rendezvous_request): If the accept fails for
+ any reason other than EINTR, call __svc_accept_failed.
+ * sunrpc/svc_udp.c (svcudp_recv): Similarly.
+ * sunrpc/svc_unix.c (rendezvous_request): Similarly.
+
2012-11-27 Siddhesh Poyarekar <siddhesh@redhat.com>
[BZ #11741]
diff --git a/sunrpc/rpc/svc.h b/sunrpc/rpc/svc.h
index 54d1ac1..b324b74 100644
--- a/sunrpc/rpc/svc.h
+++ b/sunrpc/rpc/svc.h
@@ -316,4 +316,5 @@ extern SVCXPRT *svcunix_create (int __sock, u_int __sendsize, u_int __recvsize,
__END_DECLS
+extern void __svc_accept_failed (void) attribute_hidden;
#endif /* rpc/svc.h */
diff --git a/sunrpc/svc.c b/sunrpc/svc.c
index 103770a..e48be67 100644
--- a/sunrpc/svc.c
+++ b/sunrpc/svc.c
@@ -41,6 +41,7 @@
#include <rpc/svc.h>
#include <rpc/pmap_clnt.h>
#include <sys/poll.h>
+#include <time.h>
#ifdef _RPC_THREAD_SAFE_
#define xports RPC_THREAD_VARIABLE(svc_xports_s)
@@ -544,6 +545,21 @@ svc_getreq_common (const int fd)
}
libc_hidden_nolink_sunrpc (svc_getreq_common, GLIBC_2_2)
+/* If there are no file descriptors available, then accept will fail.
+ We want to delay here so the connection request can be dequeued;
+ otherwise we can bounce between polling and accepting, never giving the
+ request a chance to dequeue and eating an enormous amount of cpu time
+ in svc_run if we're polling on many file descriptors. */
+void
+__svc_accept_failed (void)
+{
+ if (errno == EMFILE)
+ {
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 50000000 };
+ __nanosleep (&ts, NULL);
+ }
+}
+
#ifdef _RPC_THREAD_SAFE_
void
diff --git a/sunrpc/svc_tcp.c b/sunrpc/svc_tcp.c
index eb61549..93f2ae2 100644
--- a/sunrpc/svc_tcp.c
+++ b/sunrpc/svc_tcp.c
@@ -247,6 +247,7 @@ again:
{
if (errno == EINTR)
goto again;
+ __svc_accept_failed ();
return FALSE;
}
/*
diff --git a/sunrpc/svc_udp.c b/sunrpc/svc_udp.c
index 6c4d75a..3bd718b 100644
--- a/sunrpc/svc_udp.c
+++ b/sunrpc/svc_udp.c
@@ -277,8 +277,12 @@ again:
(int) su->su_iosz, 0,
(struct sockaddr *) &(xprt->xp_raddr), &len);
xprt->xp_addrlen = len;
- if (rlen == -1 && errno == EINTR)
- goto again;
+ if (rlen == -1)
+ {
+ if (errno == EINTR)
+ goto again;
+ __svc_accept_failed ();
+ }
if (rlen < 16) /* < 4 32-bit ints? */
return FALSE;
xdrs->x_op = XDR_DECODE;
diff --git a/sunrpc/svc_unix.c b/sunrpc/svc_unix.c
index 94507b2..5de09c6 100644
--- a/sunrpc/svc_unix.c
+++ b/sunrpc/svc_unix.c
@@ -244,6 +244,7 @@ again:
{
if (errno == EINTR)
goto again;
+ __svc_accept_failed ();
return FALSE;
}
/*
More information about the Libc-alpha
mailing list