diff --git a/ChangeLog.d/net_poll-fd_setsize.txt b/ChangeLog.d/net_poll-fd_setsize.txt new file mode 100644 index 000000000..e4db8c7e3 --- /dev/null +++ b/ChangeLog.d/net_poll-fd_setsize.txt @@ -0,0 +1,4 @@ +Security + * Fix a stack buffer overflow with mbedtls_net_poll() and + mbedtls_net_recv_timeout() when given a file descriptor that is + beyond FD_SETSIZE. Reported by FigBug in #4169. diff --git a/include/mbedtls/net_sockets.h b/include/mbedtls/net_sockets.h index 00fea7db1..c6e1a0270 100644 --- a/include/mbedtls/net_sockets.h +++ b/include/mbedtls/net_sockets.h @@ -151,6 +151,7 @@ int mbedtls_net_connect( mbedtls_net_context *ctx, const char *host, const char * * \return 0 if successful, or one of: * MBEDTLS_ERR_NET_SOCKET_FAILED, + * MBEDTLS_ERR_NET_UNKNOWN_HOST, * MBEDTLS_ERR_NET_BIND_FAILED, * MBEDTLS_ERR_NET_LISTEN_FAILED * @@ -170,6 +171,8 @@ int mbedtls_net_bind( mbedtls_net_context *ctx, const char *bind_ip, const char * can be NULL if client_ip is null * * \return 0 if successful, or + * MBEDTLS_ERR_NET_SOCKET_FAILED, + * MBEDTLS_ERR_NET_BIND_FAILED, * MBEDTLS_ERR_NET_ACCEPT_FAILED, or * MBEDTLS_ERR_NET_BUFFER_TOO_SMALL if buf_size is too small, * MBEDTLS_ERR_SSL_WANT_READ if bind_fd was set to @@ -182,6 +185,10 @@ int mbedtls_net_accept( mbedtls_net_context *bind_ctx, /** * \brief Check and wait for the context to be ready for read/write * + * \note The current implementation of this function uses + * select() and returns an error if the file descriptor + * is \c FD_SETSIZE or greater. + * * \param ctx Socket to check * \param rw Bitflag composed of MBEDTLS_NET_POLL_READ and * MBEDTLS_NET_POLL_WRITE specifying the events @@ -263,16 +270,21 @@ int mbedtls_net_send( void *ctx, const unsigned char *buf, size_t len ); * 'timeout' seconds. If no error occurs, the actual amount * read is returned. * + * \note The current implementation of this function uses + * select() and returns an error if the file descriptor + * is \c FD_SETSIZE or greater. + * * \param ctx Socket * \param buf The buffer to write to * \param len Maximum length of the buffer * \param timeout Maximum number of milliseconds to wait for data * 0 means no timeout (wait forever) * - * \return the number of bytes received, - * or a non-zero error code: - * MBEDTLS_ERR_SSL_TIMEOUT if the operation timed out, + * \return The number of bytes received if successful. + * MBEDTLS_ERR_SSL_TIMEOUT if the operation timed out. * MBEDTLS_ERR_SSL_WANT_READ if interrupted by a signal. + * Another negative error code (MBEDTLS_ERR_NET_xxx) + * for other failures. * * \note This function will block (until data becomes available or * timeout is reached) even if the socket is set to diff --git a/library/net_sockets.c b/library/net_sockets.c index 113040826..671115f15 100644 --- a/library/net_sockets.c +++ b/library/net_sockets.c @@ -496,6 +496,13 @@ int mbedtls_net_poll( mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout ) if( fd < 0 ) return( MBEDTLS_ERR_NET_INVALID_CONTEXT ); + /* A limitation of select() is that it only works with file descriptors + * that are strictly less than FD_SETSIZE. This is a limitation of the + * fd_set type. Error out early, because attempting to call FD_SET on a + * large file descriptor is a buffer overflow on typical platforms. */ + if( fd >= FD_SETSIZE ) + return( MBEDTLS_ERR_NET_POLL_FAILED ); + #if defined(__has_feature) #if __has_feature(memory_sanitizer) /* Ensure that memory sanitizers consider read_fds and write_fds as @@ -615,6 +622,13 @@ int mbedtls_net_recv_timeout( void *ctx, unsigned char *buf, if( fd < 0 ) return( MBEDTLS_ERR_NET_INVALID_CONTEXT ); + /* A limitation of select() is that it only works with file descriptors + * that are strictly less than FD_SETSIZE. This is a limitation of the + * fd_set type. Error out early, because attempting to call FD_SET on a + * large file descriptor is a buffer overflow on typical platforms. */ + if( fd >= FD_SETSIZE ) + return( MBEDTLS_ERR_NET_POLL_FAILED ); + FD_ZERO( &read_fds ); FD_SET( fd, &read_fds );