curl_multi_fdset: avoid FD_SET out of bounds
authorDaniel Stenberg <daniel@haxx.se>
Sun, 25 Sep 2011 15:34:12 +0000 (17:34 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Sun, 25 Sep 2011 15:34:12 +0000 (17:34 +0200)
If a socket is larger than FD_SETSIZE, avoid using FD_SET() on the
platforms where this is possible.

Bug: http://curl.haxx.se/bug/view.cgi?id=3413274
Reported by: Tim Starling

docs/libcurl/curl_multi_fdset.3
lib/multi.c
lib/select.c
lib/select.h

index fadc538..bdb389f 100644 (file)
@@ -53,6 +53,14 @@ When doing select(), you should use \fBcurl_multi_timeout\fP to figure out how
 long to wait for action. Call \fIcurl_multi_perform\fP even if no activity has
 been seen on the fd_sets after the timeout expires as otherwise internal
 retries and timeouts may not work as you'd think and want.
+
+If one of the sockets used by libcurl happens to be larger than what can be
+set in an fd_set, which on POSIX systems means that the file descriptor is
+larger than FD_SETSIZE, then libcurl will try to not set it. Setting a too
+large file descriptor in an fd_set implies an out of bounds write which can
+cause crashes, or worse. The effect of NOT storing it will possibly save you
+from the crash, but will make your program NOT wait for sockets it should wait
+for...
 .SH RETURN VALUE
 CURLMcode type, general libcurl multi interface error code. See
 \fIlibcurl-errors(3)\fP
index 5a1cef4..13f5619 100644 (file)
@@ -42,6 +42,7 @@
 #include "timeval.h"
 #include "http.h"
 #include "warnless.h"
+#include "select.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -907,11 +908,11 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle,
     for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
       curl_socket_t s = CURL_SOCKET_BAD;
 
-      if(bitmap & GETSOCK_READSOCK(i)) {
+      if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) {
         FD_SET(sockbunch[i], read_fd_set);
         s = sockbunch[i];
       }
-      if(bitmap & GETSOCK_WRITESOCK(i)) {
+      if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) {
         FD_SET(sockbunch[i], write_fd_set);
         s = sockbunch[i];
       }
index 79475d5..6cecbf7 100644 (file)
 #include "select.h"
 #include "warnless.h"
 
-/* Winsock and TPF sockets are not in range [0..FD_SETSIZE-1] */
-
-#if defined(USE_WINSOCK) || defined(TPF)
-#define VERIFY_SOCK(x) Curl_nop_stmt
-#else
-#define VALID_SOCK(s) (((s) >= 0) && ((s) < FD_SETSIZE))
-#define VERIFY_SOCK(x) do { \
-  if(!VALID_SOCK(x)) { \
-    SET_SOCKERRNO(EINVAL); \
-    return -1; \
-  } \
-} WHILE_FALSE
-#endif
-
 /* Convenience local macros */
 
 #define elapsed_ms  (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
index d785fb3..56729a3 100644 (file)
@@ -96,4 +96,20 @@ int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes,
                        fd_set* excepts, struct timeval* tv);
 #endif
 
+/* Winsock and TPF sockets are not in range [0..FD_SETSIZE-1], which
+   unfortunately makes it impossible for us to easily check if they're valid
+*/
+#if defined(USE_WINSOCK) || defined(TPF)
+#define VALID_SOCK(x) 1
+#define VERIFY_SOCK(x) Curl_nop_stmt
+#else
+#define VALID_SOCK(s) (((s) >= 0) && ((s) < FD_SETSIZE))
+#define VERIFY_SOCK(x) do { \
+  if(!VALID_SOCK(x)) { \
+    SET_SOCKERRNO(EINVAL); \
+    return -1; \
+  } \
+} WHILE_FALSE
+#endif
+
 #endif /* __SELECT_H */