Description: Use /dev/null in invalidate_fd to avoid infinite loop in OpenSSL Thanks to Andy Bakun / @thwarted for identifying the issue and suggesting the /dev/null workaround. Author: Aaron Stone Origin: upstream,https://github.com/brianmario/mysql2/commit/fc30a7c056e63517f5f66702016941b3902ec0b6.patch Reviewed-by: Cédric Boutillier Last-Update: 2014-08-24 --- a/ext/mysql2/client.c +++ b/ext/mysql2/client.c @@ -167,26 +167,30 @@ #ifndef _WIN32 /* - * Redirect clientfd to a dummy socket for mysql_close to - * write, shutdown, and close on as a no-op. - * We do this hack because we want to call mysql_close to release - * memory, but do not want mysql_close to drop connections in the - * parent if the socket got shared in fork. + * Redirect clientfd to /dev/null for mysql_close and SSL_close to write, + * shutdown, and close. The hack is needed to prevent shutdown() from breaking + * a socket that may be in use by the parent or other processes after fork. + * + * /dev/null is used to absorb writes; previously a dummy socket was used, but + * it could not abosrb writes and caused openssl to go into an infinite loop. + * * Returns Qtrue or Qfalse (success or failure) + * + * Note: if this function is needed on Windows, use "nul" instead of "/dev/null" */ static VALUE invalidate_fd(int clientfd) { #ifdef SOCK_CLOEXEC /* Atomically set CLOEXEC on the new FD in case another thread forks */ - int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + int sockfd = open("/dev/null", O_RDWR | O_CLOEXEC); if (sockfd < 0) { /* Maybe SOCK_CLOEXEC is defined but not available on this kernel */ - int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + int sockfd = open("/dev/null", O_RDWR); fcntl(sockfd, F_SETFD, FD_CLOEXEC); } #else /* Well we don't have SOCK_CLOEXEC, so just set FD_CLOEXEC quickly */ - int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + int sockfd = open("/dev/null", O_RDWR); fcntl(sockfd, F_SETFD, FD_CLOEXEC); #endif