Fix #78210: Invalid pointer address

This is actually about three distinct issues:

* If an empty string is passed as $address to `stream_socket_sendto()`,
  the `sa` is not initialized, so we must not pass it as `addr` to
  `php_stream_xport_sendto()`.

* On POSIX, `recvfrom()` truncates messages which are too long to fit
  into the specified buffer (unless `MSG_PEEK` is given), discards the
  excessive bytes, and returns the buffer length.  On Windows, the same
  happens, but `recvfrom()` returns `SOCKET_ERROR` with the error code
  `WSAEMSGSIZE`.  We have to catch this for best POSIX compatibility.

* In `php_network_parse_network_address_with_port()`, we have to zero
  `in6` (not only its alias `sa`) to properly support IPv6.

Co-Authored-By: Nikita Popov <nikita.ppv@googlemail.com>
This commit is contained in:
Christoph M. Becker 2020-03-11 10:16:04 +01:00
parent 2462f2dab1
commit 53797c206a
4 changed files with 12 additions and 3 deletions

1
NEWS
View File

@ -4,6 +4,7 @@ PHP NEWS
- Core: - Core:
. Fixed bug #79364 (When copy empty array, next key is unspecified). (cmb) . Fixed bug #79364 (When copy empty array, next key is unspecified). (cmb)
. Fixed bug #78210 (Invalid pointer address). (cmb, Nikita)
- Spl: - Spl:
. Fixed bug #75673 (SplStack::unserialize() behavior). (cmb) . Fixed bug #75673 (SplStack::unserialize() behavior). (cmb)

View File

@ -366,7 +366,7 @@ PHP_FUNCTION(stream_socket_sendto)
} }
} }
RETURN_LONG(php_stream_xport_sendto(stream, data, datalen, (int)flags, target_addr ? &sa : NULL, sl)); RETURN_LONG(php_stream_xport_sendto(stream, data, datalen, (int)flags, target_addr_len ? &sa : NULL, sl));
} }
/* }}} */ /* }}} */

View File

@ -512,9 +512,11 @@ PHPAPI int php_network_parse_network_address_with_port(const char *addr, zend_lo
zend_string *errstr = NULL; zend_string *errstr = NULL;
#if HAVE_IPV6 #if HAVE_IPV6
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)sa; struct sockaddr_in6 *in6 = (struct sockaddr_in6*)sa;
#endif
memset(sa, 0, sizeof(struct sockaddr)); memset(in6, 0, sizeof(struct sockaddr_in6));
#else
memset(in4, 0, sizeof(struct sockaddr_in));
#endif
if (*addr == '[') { if (*addr == '[') {
colon = memchr(addr + 1, ']', addrlen-1); colon = memchr(addr + 1, ']', addrlen-1);

View File

@ -272,6 +272,12 @@ static inline int sock_recvfrom(php_netstream_data_t *sock, char *buf, size_t bu
socklen_t sl = sizeof(sa); socklen_t sl = sizeof(sa);
ret = recvfrom(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, (struct sockaddr*)&sa, &sl); ret = recvfrom(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, (struct sockaddr*)&sa, &sl);
ret = (ret == SOCK_CONN_ERR) ? -1 : ret; ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
#ifdef PHP_WIN32
/* POSIX discards excess bytes without signalling failure; emulate this on Windows */
if (ret == -1 && WSAGetLastError() == WSAEMSGSIZE) {
ret = buflen;
}
#endif
if (sl) { if (sl) {
php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl, php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
textaddr, addr, addrlen); textaddr, addr, addrlen);