From 4312a446d0a101ffa7fa5d4c624e487a32706b59 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 27 Nov 2025 19:37:01 +0000 Subject: [PATCH] Fix GH-20601: ftp_connect() timeout argument overflow. close GH-20603 --- NEWS | 3 +++ ext/ftp/php_ftp.c | 7 +++++++ ext/ftp/tests/gh20601.phpt | 19 +++++++++++++++++++ main/network.c | 2 ++ 4 files changed, 31 insertions(+) create mode 100644 ext/ftp/tests/gh20601.phpt diff --git a/NEWS b/NEWS index 214f1105b5c2a..3ec3f8c096d03 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,9 @@ PHP NEWS . Fixed bug GH-20483 (ASAN stack overflow with fiber.stack_size INI small value). (David Carlier) +- FTP: + . Fixed bug GH-20601 (ftp_connect overflow on timeout). (David Carlier) + - GD: . Fixed bug GH-20511 (imagegammacorrect out of range input/output values). (David Carlier) diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c index 4d33e4d82535a..0df3aa6e75524 100644 --- a/ext/ftp/php_ftp.c +++ b/ext/ftp/php_ftp.c @@ -158,11 +158,18 @@ PHP_FUNCTION(ftp_connect) RETURN_THROWS(); } + const zend_long timeoutmax = (zend_long)((double) PHP_TIMEOUT_ULL_MAX / 1000000.0); + if (timeout_sec <= 0) { zend_argument_value_error(3, "must be greater than 0"); RETURN_THROWS(); } + if (timeout_sec >= timeoutmax) { + zend_argument_value_error(3, "must be less than " ZEND_LONG_FMT, timeoutmax); + RETURN_THROWS(); + } + /* connect */ if (!(ftp = ftp_open(host, (short)port, timeout_sec))) { RETURN_FALSE; diff --git a/ext/ftp/tests/gh20601.phpt b/ext/ftp/tests/gh20601.phpt new file mode 100644 index 0000000000000..3ece7736c3aaa --- /dev/null +++ b/ext/ftp/tests/gh20601.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-20601 (ftp_connect timeout overflow) +--EXTENSIONS-- +ftp +--SKIPIF-- + +--FILE-- +getMessage(); +} +?> +--EXPECTF-- +ftp_connect(): Argument #3 ($timeout) must be less than %d diff --git a/main/network.c b/main/network.c index fecec0545e8f0..7b69614d533dd 100644 --- a/main/network.c +++ b/main/network.c @@ -319,6 +319,8 @@ static inline void php_network_set_limit_time(struct timeval *limit_time, struct timeval *timeout) { gettimeofday(limit_time, NULL); + const double timeoutmax = (double) PHP_TIMEOUT_ULL_MAX / 1000000.0; + ZEND_ASSERT(limit_time->tv_sec < (timeoutmax - timeout->tv_sec)); limit_time->tv_sec += timeout->tv_sec; limit_time->tv_usec += timeout->tv_usec; if (limit_time->tv_usec >= 1000000) {