Skip to content

Commit 412a6b2

Browse files
committed
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.
1 parent 9999a5b commit 412a6b2

File tree

2 files changed

+51
-8
lines changed

2 files changed

+51
-8
lines changed

NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.3.17
44

5+
- FTP:
6+
. Fixed bug GH-16800 (ftp functions can abort with EINTR). (nielsdos)
57

68
02 Jan 2025, PHP 8.3.16RC1
79

ext/ftp/ftp.c

+49-8
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,22 @@ ftp_getresp(ftpbuf_t *ftp)
13871387
}
13881388
/* }}} */
13891389

1390+
static ssize_t my_send_wrapper_with_restart(php_socket_t fd, const void *buf, size_t size, int flags) {
1391+
ssize_t n;
1392+
do {
1393+
n = send(fd, buf, size, flags);
1394+
} while (n == -1 && php_socket_errno() == EINTR);
1395+
return n;
1396+
}
1397+
1398+
static ssize_t my_recv_wrapper_with_restart(php_socket_t fd, void *buf, size_t size, int flags) {
1399+
ssize_t n;
1400+
do {
1401+
n = recv(fd, buf, size, flags);
1402+
} while (n == -1 && php_socket_errno() == EINTR);
1403+
return n;
1404+
}
1405+
13901406
int single_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t size) {
13911407
#ifdef HAVE_FTP_SSL
13921408
int err;
@@ -1402,7 +1418,7 @@ int single_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t size) {
14021418
handle = ftp->data->ssl_handle;
14031419
fd = ftp->data->fd;
14041420
} else {
1405-
return send(s, buf, size, 0);
1421+
return my_send_wrapper_with_restart(s, buf, size, 0);
14061422
}
14071423

14081424
do {
@@ -1441,8 +1457,33 @@ int single_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t size) {
14411457
} while (retry);
14421458
return sent;
14431459
#else
1444-
return send(s, buf, size, 0);
1460+
return my_send_wrapper_with_restart(s, buf, size, 0);
1461+
#endif
1462+
}
1463+
1464+
static int my_poll(php_socket_t fd, int events, int timeout) {
1465+
int n;
1466+
zend_hrtime_t timeout_hr = (zend_hrtime_t) timeout * 1000000;
1467+
1468+
while (true) {
1469+
zend_hrtime_t start_ns = zend_hrtime();
1470+
n = php_pollfd_for_ms(fd, events, (int) (timeout_hr / 1000000));
1471+
1472+
if (n == -1 && php_socket_errno() == EINTR) {
1473+
zend_hrtime_t delta_ns = zend_hrtime() - start_ns;
1474+
if (delta_ns > timeout_hr) {
1475+
#ifndef PHP_WIN32
1476+
errno = ETIMEDOUT;
14451477
#endif
1478+
break;
1479+
}
1480+
timeout_hr -= delta_ns;
1481+
} else {
1482+
break;
1483+
}
1484+
}
1485+
1486+
return n;
14461487
}
14471488

14481489
/* {{{ my_send */
@@ -1454,7 +1495,7 @@ my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
14541495

14551496
size = len;
14561497
while (size) {
1457-
n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
1498+
n = my_poll(s, POLLOUT, ftp->timeout_sec * 1000);
14581499

14591500
if (n < 1) {
14601501
char buf[256];
@@ -1493,7 +1534,7 @@ my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
14931534
SSL *handle = NULL;
14941535
php_socket_t fd;
14951536
#endif
1496-
n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
1537+
n = my_poll(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
14971538
if (n < 1) {
14981539
char buf[256];
14991540
if (n == 0) {
@@ -1553,7 +1594,7 @@ my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
15531594
} while (retry);
15541595
} else {
15551596
#endif
1556-
nr_bytes = recv(s, buf, len, 0);
1597+
nr_bytes = my_recv_wrapper_with_restart(s, buf, len, 0);
15571598
#ifdef HAVE_FTP_SSL
15581599
}
15591600
#endif
@@ -1567,7 +1608,7 @@ data_available(ftpbuf_t *ftp, php_socket_t s)
15671608
{
15681609
int n;
15691610

1570-
n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000);
1611+
n = my_poll(s, PHP_POLLREADABLE, 1000);
15711612
if (n < 1) {
15721613
char buf[256];
15731614
if (n == 0) {
@@ -1590,7 +1631,7 @@ data_writeable(ftpbuf_t *ftp, php_socket_t s)
15901631
{
15911632
int n;
15921633

1593-
n = php_pollfd_for_ms(s, POLLOUT, 1000);
1634+
n = my_poll(s, POLLOUT, 1000);
15941635
if (n < 1) {
15951636
char buf[256];
15961637
if (n == 0) {
@@ -1614,7 +1655,7 @@ my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrl
16141655
{
16151656
int n;
16161657

1617-
n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
1658+
n = my_poll(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
16181659
if (n < 1) {
16191660
char buf[256];
16201661
if (n == 0) {

0 commit comments

Comments
 (0)