From 233115ea236fd1aa258e8b733d3855d1261fba8b Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Thu, 31 Mar 2016 12:39:01 +0100 Subject: [PATCH] fix #71609: Segmentation fault on ZTS with gethostbyname --- TSRM/m4/gethostbyname.m4 | 197 ++++++++++++++++++++++++++++++++++++ TSRM/tsrm.m4 | 4 +- ext/sockets/sockaddr_conv.c | 2 +- ext/sockets/sockets.c | 4 +- ext/standard/dns.c | 4 +- ext/standard/file.c | 5 + ext/standard/file.h | 5 + main/fastcgi.c | 2 +- main/network.c | 94 ++++++++++++++++- main/php_network.h | 6 ++ 10 files changed, 314 insertions(+), 9 deletions(-) create mode 100644 TSRM/m4/gethostbyname.m4 diff --git a/TSRM/m4/gethostbyname.m4 b/TSRM/m4/gethostbyname.m4 new file mode 100644 index 0000000000..ac3eb06018 --- /dev/null +++ b/TSRM/m4/gethostbyname.m4 @@ -0,0 +1,197 @@ +# ================================================================================= +# http://www.gnu.org/software/autoconf-archive/ax_func_which_gethostbyname_r.html +# ================================================================================= +# +# SYNOPSIS +# +# AX_FUNC_WHICH_GETHOSTBYNAME_R +# +# DESCRIPTION +# +# Determines which historical variant of the gethostbyname_r() call +# (taking three, five, or six arguments) is available on the system and +# defines one of the following macros accordingly: +# +# HAVE_FUNC_GETHOSTBYNAME_R_6 +# HAVE_FUNC_GETHOSTBYNAME_R_5 +# HAVE_FUNC_GETHOSTBYNAME_R_3 +# +# as well as +# +# HAVE_GETHOSTBYNAME_R +# +# If used in conjunction with gethostname.c, the API demonstrated in +# test.c can be used regardless of which gethostbyname_r() is available. +# These example files can be found at +# http://www.csn.ul.ie/~caolan/publink/gethostbyname_r +# +# based on David Arnold's autoconf suggestion in the threads faq +# +# Originally named "AC_caolan_FUNC_WHICH_GETHOSTBYNAME_R". Rewritten for +# Autoconf 2.5x, and updated for 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Caolan McNamara +# Copyright (c) 2008 Daniel Richard G. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 7 + +AC_DEFUN([AX_FUNC_WHICH_GETHOSTBYNAME_R], [ + + AC_LANG_PUSH([C]) + AC_MSG_CHECKING([how many arguments gethostbyname_r() takes]) + + AC_CACHE_VAL([ac_cv_func_which_gethostbyname_r], [ + +################################################################ + +ac_cv_func_which_gethostbyname_r=unknown + +# +# ONE ARGUMENT (sanity check) +# + +# This should fail, as there is no variant of gethostbyname_r() that takes +# a single argument. If it actually compiles, then we can assume that +# netdb.h is not declaring the function, and the compiler is thereby +# assuming an implicit prototype. In which case, we're out of luck. +# +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], + [ + char *name = "www.gnu.org"; + (void)gethostbyname_r(name) /* ; */ + ])], + [ac_cv_func_which_gethostbyname_r=no]) + +# +# SIX ARGUMENTS +# (e.g. Linux) +# + +if test "$ac_cv_func_which_gethostbyname_r" = "unknown"; then + +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], + [ + char *name = "www.gnu.org"; + struct hostent ret, *retp; + char buf@<:@1024@:>@; + int buflen = 1024; + int my_h_errno; + (void)gethostbyname_r(name, &ret, buf, buflen, &retp, &my_h_errno) /* ; */ + ])], + [ac_cv_func_which_gethostbyname_r=six]) + +fi + +# +# FIVE ARGUMENTS +# (e.g. Solaris) +# + +if test "$ac_cv_func_which_gethostbyname_r" = "unknown"; then + +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], + [ + char *name = "www.gnu.org"; + struct hostent ret; + char buf@<:@1024@:>@; + int buflen = 1024; + int my_h_errno; + (void)gethostbyname_r(name, &ret, buf, buflen, &my_h_errno) /* ; */ + ])], + [ac_cv_func_which_gethostbyname_r=five]) + +fi + +# +# THREE ARGUMENTS +# (e.g. AIX, HP-UX, Tru64) +# + +if test "$ac_cv_func_which_gethostbyname_r" = "unknown"; then + +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], + [ + char *name = "www.gnu.org"; + struct hostent ret; + struct hostent_data data; + (void)gethostbyname_r(name, &ret, &data) /* ; */ + ])], + [ac_cv_func_which_gethostbyname_r=three]) + +fi + +################################################################ + +]) dnl end AC_CACHE_VAL + +case "$ac_cv_func_which_gethostbyname_r" in + three|five|six) + AC_DEFINE([HAVE_GETHOSTBYNAME_R], [1], + [Define to 1 if you have some form of gethostbyname_r().]) + ;; +esac + +case "$ac_cv_func_which_gethostbyname_r" in + three) + AC_MSG_RESULT([three]) + AC_DEFINE([HAVE_FUNC_GETHOSTBYNAME_R_3], [1], + [Define to 1 if you have the three-argument form of gethostbyname_r().]) + ;; + + five) + AC_MSG_RESULT([five]) + AC_DEFINE([HAVE_FUNC_GETHOSTBYNAME_R_5], [1], + [Define to 1 if you have the five-argument form of gethostbyname_r().]) + ;; + + six) + AC_MSG_RESULT([six]) + AC_DEFINE([HAVE_FUNC_GETHOSTBYNAME_R_6], [1], + [Define to 1 if you have the six-argument form of gethostbyname_r().]) + ;; + + no) + AC_MSG_RESULT([cannot find function declaration in netdb.h]) + ;; + + unknown) + AC_MSG_RESULT([can't tell]) + ;; + + *) + AC_MSG_ERROR([internal error]) + ;; +esac + +AC_LANG_POP + +]) dnl end AC_DEFUN + diff --git a/TSRM/tsrm.m4 b/TSRM/tsrm.m4 index b53a4bb805..98aa2b8c91 100644 --- a/TSRM/tsrm.m4 +++ b/TSRM/tsrm.m4 @@ -1,3 +1,4 @@ +m4_include([TSRM/m4/gethostbyname.m4]) dnl TSRM_CHECK_GCC_ARG(ARG, ACTION-IF-FOUND, ACTION-IF-NOT_FOUND) AC_DEFUN([TSRM_CHECK_GCC_ARG],[ @@ -32,6 +33,8 @@ AC_CHECK_HEADERS(stdarg.h) AC_CHECK_FUNCS(sigprocmask) +AX_FUNC_WHICH_GETHOSTBYNAME_R() + ]) @@ -89,7 +92,6 @@ else fi ]) - AC_DEFUN([TSRM_THREADS_CHECKS],[ dnl For the thread implementations, we always use --with-* diff --git a/ext/sockets/sockaddr_conv.c b/ext/sockets/sockaddr_conv.c index 73a0085720..1ce109ee8c 100644 --- a/ext/sockets/sockaddr_conv.c +++ b/ext/sockets/sockaddr_conv.c @@ -90,7 +90,7 @@ int php_set_inet_addr(struct sockaddr_in *sin, char *string, php_socket *php_soc if (inet_aton(string, &tmp)) { sin->sin_addr.s_addr = tmp.s_addr; } else { - if (strlen(string) > MAXFQDNLEN || ! (host_entry = gethostbyname(string))) { + if (strlen(string) > MAXFQDNLEN || ! (host_entry = php_network_gethostbyname(string))) { /* Note: < -10000 indicates a host lookup error */ #ifdef PHP_WIN32 PHP_SOCKET_ERROR(php_sock, "Host lookup failed", WSAGetLastError()); diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index a207225f1c..6039ac65ba 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -425,9 +425,9 @@ static int php_open_listen_sock(php_socket **php_sock, int port, int backlog) /* *php_sock = sock; #ifndef PHP_WIN32 - if ((hp = gethostbyname("0.0.0.0")) == NULL) { + if ((hp = php_network_gethostbyname("0.0.0.0")) == NULL) { #else - if ((hp = gethostbyname("localhost")) == NULL) { + if ((hp = php_network_gethostbyname("localhost")) == NULL) { #endif efree(sock); return 0; diff --git a/ext/standard/dns.c b/ext/standard/dns.c index a3394e0479..de40649e69 100644 --- a/ext/standard/dns.c +++ b/ext/standard/dns.c @@ -251,7 +251,7 @@ PHP_FUNCTION(gethostbynamel) RETURN_FALSE; } - hp = gethostbyname(hostname); + hp = php_network_gethostbyname(hostname); if (hp == NULL || hp->h_addr_list == NULL) { RETURN_FALSE; } @@ -272,7 +272,7 @@ static zend_string *php_gethostbyname(char *name) struct in_addr in; char *address; - hp = gethostbyname(name); + hp = php_network_gethostbyname(name); if (!hp || !*(hp->h_addr_list)) { return zend_string_init(name, strlen(name), 0); diff --git a/ext/standard/file.c b/ext/standard/file.c index 26f5c161ce..cefdc4aecb 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -159,6 +159,11 @@ static void file_globals_ctor(php_file_globals *file_globals_p) static void file_globals_dtor(php_file_globals *file_globals_p) { +#if defined(HAVE_GETHOSTBYNAME_R) + if (file_globals_p->tmp_host_buf) { + free(file_globals_p->tmp_host_buf); + } +#endif } PHP_INI_BEGIN() diff --git a/ext/standard/file.h b/ext/standard/file.h index d423475386..a9b96d6b38 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -129,6 +129,11 @@ typedef struct { HashTable *stream_filters; /* per-request copy of stream_filters_hash */ HashTable *wrapper_errors; /* key: wrapper address; value: linked list of char* */ int pclose_wait; +#if defined(HAVE_GETHOSTBYNAME_R) + struct hostent tmp_host_info; + char *tmp_host_buf; + size_t tmp_host_buf_len; +#endif } php_file_globals; #ifdef ZTS diff --git a/main/fastcgi.c b/main/fastcgi.c index ce33369045..fbc6f403a0 100644 --- a/main/fastcgi.c +++ b/main/fastcgi.c @@ -692,7 +692,7 @@ int fcgi_listen(const char *path, int backlog) if(strlen(host) > MAXFQDNLEN) { hep = NULL; } else { - hep = gethostbyname(host); + hep = php_network_gethostbyname(host); } if (!hep || hep->h_addrtype != AF_INET || !hep->h_addr_list[0]) { fcgi_log(FCGI_ERROR, "Cannot resolve host name '%s'!\n", host); diff --git a/main/network.c b/main/network.c index 138f86b3e4..6d1d97bc82 100644 --- a/main/network.c +++ b/main/network.c @@ -176,6 +176,8 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka # endif struct addrinfo hints, *res, *sai; #else + char *tmp_host_buf = NULL; + struct hostent tmp_host_info; struct hostent *host_info; struct in_addr in; #endif @@ -245,12 +247,11 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka freeaddrinfo(res); #else if (!inet_aton(host, &in)) { - /* XXX NOT THREAD SAFE (is safe under win32) */ if(strlen(host) > MAXFQDNLEN) { host_info = NULL; errno = E2BIG; } else { - host_info = gethostbyname(host); + host_info = php_network_gethostbyname(host, &tmp_host_info, &tmp_host_buf); } if (host_info == NULL) { if (error_string) { @@ -271,6 +272,9 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka ((struct sockaddr_in *)*sap)->sin_addr = in; sap++; n = 1; + if (tmp_host_buf) { + efree(tmp_host_buf); + } #endif *sap = NULL; @@ -1257,9 +1261,95 @@ PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout) } return n; } +#endif + +#if defined(HAVE_GETHOSTBYNAME_R) +#ifdef HAVE_FUNC_GETHOSTBYNAME_R_6 +struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen) +{ + struct hostent *hp; + int herr,res; + + if (*hstbuflen == 0) { + *hstbuflen = 1024; + *tmphstbuf = (char *)malloc (*hstbuflen); + } + + while (( res = + gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&hp,&herr)) + && (errno == ERANGE)) { + /* Enlarge the buffer. */ + *hstbuflen *= 2; + *tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen); + } + + if (res != SUCCESS) { + return NULL; + } + + return hp; +} +#endif +#ifdef HAVE_FUNC_GETHOSTBYNAME_R_5 +struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen) +{ + struct hostent *hp; + int herr; + + if (*hstbuflen == 0) { + *hstbuflen = 1024; + *tmphstbuf = (char *)malloc (*hstbuflen); + } + + while ((NULL == ( hp = + gethostbyname_r(host,hostbuf,*tmphstbuf,*hstbuflen,&herr))) + && (errno == ERANGE)) { + /* Enlarge the buffer. */ + *hstbuflen *= 2; + *tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen); + } + return hp; +} +#endif +#ifdef HAVE_FUNC_GETHOSTBYNAME_R_3 +struct hostent * gethostname_re (const char *host,struct hostent *hostbuf,char **tmphstbuf,size_t *hstbuflen) +{ + if (*hstbuflen == 0) { + *hstbuflen = sizeof(struct hostent_data); + *tmphstbuf = (char *)malloc (*hstbuflen); + } else { + if (*hstbuflen < sizeof(struct hostent_data)) { + *hstbuflen = sizeof(struct hostent_data); + *tmphstbuf = (char *)realloc(*tmphstbuf, *hstbuflen); + } + } + memset((void *)(*tmphstbuf),0,*hstbuflen); + + if (SUCCESS != gethostbyname_r(host,hostbuf,(struct hostent_data *)*tmphstbuf)) { + return NULL; + } + return hostbuf; +} #endif +#endif + +PHPAPI struct hostent* php_network_gethostbyname(char *name) { +#if !defined(HAVE_GETHOSTBYNAME_R) + return gethostbyname(name); +#else + if (FG(tmp_host_buf)) { + free(FG(tmp_host_buf)); + } + FG(tmp_host_buf) = NULL; + FG(tmp_host_buf_len) = 0; + + memset(&FG(tmp_host_info), 0, sizeof(struct hostent)); + + return gethostname_re(name, &FG(tmp_host_info), &FG(tmp_host_buf), &FG(tmp_host_buf_len)); +#endif +} /* * Local variables: diff --git a/main/php_network.h b/main/php_network.h index daf9671132..4ddae5cd4c 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -74,6 +74,10 @@ END_EXTERN_C() #include #endif +#ifdef HAVE_GETHOSTBYNAME_R +#include +#endif + /* These are here, rather than with the win32 counterparts above, * since defines them. */ #ifndef SHUT_RD @@ -309,6 +313,8 @@ PHPAPI void php_network_populate_name_from_sockaddr( PHPAPI int php_network_parse_network_address_with_port(const char *addr, zend_long addrlen, struct sockaddr *sa, socklen_t *sl); + +PHPAPI struct hostent* php_network_gethostbyname(char *name); END_EXTERN_C() #define php_stream_sock_open_from_socket(socket, persistent) _php_stream_sock_open_from_socket((socket), (persistent) STREAMS_CC) -- 2.40.0