mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix GH-16800: ftp functions can abort with EINTR
This adds wrappers around recv(), send(), and php_pollfd_for_ms() to
handle EINTR.
This is a bit hard to test on its own, but it is testable manually using
the following script:
```php
pcntl_signal(SIGUSR1, function() {
var_dump(func_get_args());
}, false);
var_dump(getmypid());
sleep(10);
$ftp = ftp_connect('127.0.0.1');
ftp_login($ftp, 'user', 'pass');
ftp_put($ftp, 'testfile', 'testfile');
```
in combination with an infinite while loop that sends SIGUSR1 to the
process.
Closes GH-17327.
This commit is contained in:
2
NEWS
2
NEWS
@@ -2,6 +2,8 @@ PHP NEWS
|
||||
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||
?? ??? ????, PHP 8.3.17
|
||||
|
||||
- FTP:
|
||||
. Fixed bug GH-16800 (ftp functions can abort with EINTR). (nielsdos)
|
||||
|
||||
02 Jan 2025, PHP 8.3.16RC1
|
||||
|
||||
|
||||
@@ -1387,6 +1387,22 @@ ftp_getresp(ftpbuf_t *ftp)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static ssize_t my_send_wrapper_with_restart(php_socket_t fd, const void *buf, size_t size, int flags) {
|
||||
ssize_t n;
|
||||
do {
|
||||
n = send(fd, buf, size, flags);
|
||||
} while (n == -1 && php_socket_errno() == EINTR);
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t my_recv_wrapper_with_restart(php_socket_t fd, void *buf, size_t size, int flags) {
|
||||
ssize_t n;
|
||||
do {
|
||||
n = recv(fd, buf, size, flags);
|
||||
} while (n == -1 && php_socket_errno() == EINTR);
|
||||
return n;
|
||||
}
|
||||
|
||||
int single_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t size) {
|
||||
#ifdef HAVE_FTP_SSL
|
||||
int err;
|
||||
@@ -1402,7 +1418,7 @@ int single_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t size) {
|
||||
handle = ftp->data->ssl_handle;
|
||||
fd = ftp->data->fd;
|
||||
} else {
|
||||
return send(s, buf, size, 0);
|
||||
return my_send_wrapper_with_restart(s, buf, size, 0);
|
||||
}
|
||||
|
||||
do {
|
||||
@@ -1441,10 +1457,35 @@ int single_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t size) {
|
||||
} while (retry);
|
||||
return sent;
|
||||
#else
|
||||
return send(s, buf, size, 0);
|
||||
return my_send_wrapper_with_restart(s, buf, size, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int my_poll(php_socket_t fd, int events, int timeout) {
|
||||
int n;
|
||||
zend_hrtime_t timeout_hr = (zend_hrtime_t) timeout * 1000000;
|
||||
|
||||
while (true) {
|
||||
zend_hrtime_t start_ns = zend_hrtime();
|
||||
n = php_pollfd_for_ms(fd, events, (int) (timeout_hr / 1000000));
|
||||
|
||||
if (n == -1 && php_socket_errno() == EINTR) {
|
||||
zend_hrtime_t delta_ns = zend_hrtime() - start_ns;
|
||||
if (delta_ns > timeout_hr) {
|
||||
#ifndef PHP_WIN32
|
||||
errno = ETIMEDOUT;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
timeout_hr -= delta_ns;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/* {{{ my_send */
|
||||
int
|
||||
my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
|
||||
@@ -1454,7 +1495,7 @@ my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
|
||||
|
||||
size = len;
|
||||
while (size) {
|
||||
n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
|
||||
n = my_poll(s, POLLOUT, ftp->timeout_sec * 1000);
|
||||
|
||||
if (n < 1) {
|
||||
char buf[256];
|
||||
@@ -1493,7 +1534,7 @@ my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
|
||||
SSL *handle = NULL;
|
||||
php_socket_t fd;
|
||||
#endif
|
||||
n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
|
||||
n = my_poll(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
|
||||
if (n < 1) {
|
||||
char buf[256];
|
||||
if (n == 0) {
|
||||
@@ -1553,7 +1594,7 @@ my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
|
||||
} while (retry);
|
||||
} else {
|
||||
#endif
|
||||
nr_bytes = recv(s, buf, len, 0);
|
||||
nr_bytes = my_recv_wrapper_with_restart(s, buf, len, 0);
|
||||
#ifdef HAVE_FTP_SSL
|
||||
}
|
||||
#endif
|
||||
@@ -1567,7 +1608,7 @@ data_available(ftpbuf_t *ftp, php_socket_t s)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000);
|
||||
n = my_poll(s, PHP_POLLREADABLE, 1000);
|
||||
if (n < 1) {
|
||||
char buf[256];
|
||||
if (n == 0) {
|
||||
@@ -1590,7 +1631,7 @@ data_writeable(ftpbuf_t *ftp, php_socket_t s)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = php_pollfd_for_ms(s, POLLOUT, 1000);
|
||||
n = my_poll(s, POLLOUT, 1000);
|
||||
if (n < 1) {
|
||||
char buf[256];
|
||||
if (n == 0) {
|
||||
@@ -1614,7 +1655,7 @@ my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrl
|
||||
{
|
||||
int n;
|
||||
|
||||
n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
|
||||
n = my_poll(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
|
||||
if (n < 1) {
|
||||
char buf[256];
|
||||
if (n == 0) {
|
||||
|
||||
Reference in New Issue
Block a user