Skip to content

Conversation

@devnexen
Copy link
Member

No description provided.

@devnexen devnexen marked this pull request as ready for review November 28, 2025 20:26
@devnexen devnexen requested a review from bukka as a code owner November 28, 2025 20:26
@devnexen devnexen linked an issue Nov 28, 2025 that may be closed by this pull request
struct timeval *timeout)
{
gettimeofday(limit_time, NULL);
const double timeoutmax = (double) PHP_TIMEOUT_ULL_MAX / 1000000.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just checking the whole logic here and I wonder why PHP_TIMEOUT_ULL_MAX is unsigned int 64? The time_t is signed int 64 (or 32 on 32bit arch) if I'm not mistakend. And in this case this is later converted to int (see php_pollfd_for and mainly php_tvtoto). So what am I missing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it s unsigned but my sense since it s that way because this constant is never used directly but converted from microseconds value to seconds (so yes unsigned int64 > double > time_t or int).

Copy link
Member

@bukka bukka Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But how is this covering the lower type overflows? I mean if it gets converted in php_tvtoto, then it will still overflow on conversion to int, right?

main/network.c Outdated
gettimeofday(limit_time, NULL);
const double timeoutmax = (double) PHP_TIMEOUT_ULL_MAX / 1000000.0;

if (limit_time->tv_sec > (timeoutmax - timeout->tv_sec)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't that be be >=?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it should

main/network.c Outdated
php_network_set_limit_time(&limit_time, &working_timeout);
if (UNEXPECTED(php_network_set_limit_time(&limit_time, &working_timeout) == FAILURE)) {
php_network_freeaddresses(psal);
zend_value_error("timeout value overflow");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about throwing error here even though it already triggers normal errors in some cases.

Why is this even checked here and not in the actual function as it has been case before?

main/network.c Outdated
Comment on lines 324 to 327
if (limit_time->tv_sec >= (timeoutmax - timeout->tv_sec)) {
zend_value_error("timeout value overflow");
return FAILURE;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather have this be an assertion and make it the responsibility of the calling code to ensure this is respected. So move the check to ftp_connect(), especially as you get better error messages and you can pin point to the exact argument is causing an issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok done, note that I purposely kept separated the check for <= 0 and the new one since it s stable branch.

Comment on lines 863 to 868
#if HAVE_GETTIMEOFDAY
php_network_set_limit_time(&limit_time, &working_timeout);
if (UNEXPECTED(php_network_set_limit_time(&limit_time, &working_timeout) == FAILURE)) {
php_network_freeaddresses(psal);
return -1;
}
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

Comment on lines 401 to 407
#if HAVE_GETTIMEOFDAY
php_network_set_limit_time(&limit_time, &working_timeout);
if (UNEXPECTED(php_network_set_limit_time(&limit_time, &working_timeout) == FAILURE)) {
error = ERANGE;
ret = -1;
goto ok;
}
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

Comment on lines +5 to +9
--SKIPIF--
<?php
if (PHP_INT_SIZE != 8) die("skip: 64-bit only");
if (PHP_OS_FAMILY === 'Windows') die("skip not for windows");
?>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This an issue that cannot appear on 32 bit nor Windows?

Copy link
Member Author

@devnexen devnexen Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes this test "failed" for both earlier.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright

@devnexen devnexen closed this in 4312a44 Nov 29, 2025
@iluuu1994
Copy link
Member

iluuu1994 commented Dec 2, 2025

@devnexen Can you please look at the FTP failure in https://github.com/php/php-src/actions/runs/19843698497/job/56857155631? It looks related to this change.

@devnexen
Copy link
Member Author

devnexen commented Dec 2, 2025

ok will have a look tonight.

@iluuu1994
Copy link
Member

@devnexen @ndossche also reminded me that this needs to be cherry-picked for 8.5.1, given we've just tagged RC1. /cc @php/release-managers-85

@bukka
Copy link
Member

bukka commented Dec 2, 2025

8.3.29RC1 also tagged and win32 NTS is failing there as well so we will likely need another RC (ideally next week as @edorian does it already for 8.5.1 due to another fix) there as well. CC @ERICMAN

There's still time for 8.4 as Saki is delaying tag till tomorrow.

devnexen added a commit to devnexen/php-src that referenced this pull request Dec 2, 2025
the timeout needed to be unsigned.
devnexen added a commit that referenced this pull request Dec 3, 2025
the timeout needed to be unsigned.

close GH-20634
devnexen added a commit that referenced this pull request Dec 3, 2025
* PHP-8.3:
  Fix GH-20603 issue on windows 32 bits.
devnexen added a commit that referenced this pull request Dec 3, 2025
* PHP-8.4:
  Fix GH-20603 issue on windows 32 bits.
devnexen added a commit that referenced this pull request Dec 3, 2025
* PHP-8.5:
  Fix GH-20603 issue on windows 32 bits.
@devnexen
Copy link
Member Author

devnexen commented Dec 3, 2025

@devnexen @ndossche also reminded me that this needs to be cherry-picked for 8.5.1, given we've just tagged RC1. /cc @php/release-managers-85

Ok fix committed. Cheers.

edorian pushed a commit that referenced this pull request Dec 9, 2025
the timeout needed to be unsigned.

close GH-20634
bukka pushed a commit that referenced this pull request Dec 12, 2025
the timeout needed to be unsigned.

close GH-20634

(cherry picked from commit ff51ac1)
bukka pushed a commit that referenced this pull request Dec 12, 2025
the timeout needed to be unsigned.

close GH-20634

(cherry picked from commit ff51ac1)
RETURN_THROWS();
}

const zend_long timeoutmax = (zend_long)((double) PHP_TIMEOUT_ULL_MAX / 1000000.0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is causing an integer overflow on 32-bit PHP, resulting in

Fatal error: Uncaught ValueError: ftp_connect(): Argument #3 ($timeout) must be less than -2147483648

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

been fixed for the next release.

@iluuu1994
Copy link
Member

Shouldn't this have been cherry-picked for each release?

@ndossche
Copy link
Member

ndossche commented Jan 2, 2026

Since these are integer UB overflow fixes, the real world impact of these bugs are very small. So wouldn't it make more sense to only do these in master in the future to avoid risking regressions? If necessary, they can always be cherry-picked later.

@iluuu1994
Copy link
Member

iluuu1994 commented Jan 2, 2026

It looks like multiple things went wrong here.

  • As @ndossche mentioned, we might want to be more cautious when pushing fixes for fuzzing issues (i.e. push them to master only). They are frequently cosmetic and not worth the risk of causing real-world issues though a faulty fix.
  • The issue was found and fixed in time for the release, but not actually deployed. @bukka pinged some people, but only mentioned @SakiTakamachi by-name, and misspelled @ericmann's usename. Optimally, all relevant RM teams should be pinged through the team's handle.
  • Even the RM team that was pinged (@php/release-managers-85) failed to cherry-pick the fix. Nobody even acknowledged the ping.
  • Ultimately, the author of the faulty commit (@devnexen in this case) should ensure the fix actually lands.

My intention is not to blame anybody here (nobody is exempt from causing bugs). But maybe we can put more guards in place to avoid this in the future. The RC-period is quite pointless if issues that are found aren't actually fixed.

Another issue is that deploying such a fix requires the intervention of 3 RM teams. That doesn't exactly increase the chance of everything going smoothly. Maybe it's acceptable for one RM team to cherry-pick the fix for every release? I'm not an RM and don't plan to be, so feel free to suggest something more sensible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

integer overflow in network.c

6 participants