-
Notifications
You must be signed in to change notification settings - Fork 8k
Fix GH-20601: ftp_connect() timeout argument overflow. #20603
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| struct timeval *timeout) | ||
| { | ||
| gettimeofday(limit_time, NULL); | ||
| const double timeoutmax = (double) PHP_TIMEOUT_ULL_MAX / 1000000.0; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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 >=?
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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
| if (limit_time->tv_sec >= (timeoutmax - timeout->tv_sec)) { | ||
| zend_value_error("timeout value overflow"); | ||
| return FAILURE; | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| #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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
| #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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
| --SKIPIF-- | ||
| <?php | ||
| if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); | ||
| if (PHP_OS_FAMILY === 'Windows') die("skip not for windows"); | ||
| ?> |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright
|
@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. |
|
ok will have a look tonight. |
the timeout needed to be unsigned.
the timeout needed to be unsigned. close GH-20634
* PHP-8.3: Fix GH-20603 issue on windows 32 bits.
* PHP-8.4: Fix GH-20603 issue on windows 32 bits.
* PHP-8.5: Fix GH-20603 issue on windows 32 bits.
the timeout needed to be unsigned. close GH-20634
| RETURN_THROWS(); | ||
| } | ||
|
|
||
| const zend_long timeoutmax = (zend_long)((double) PHP_TIMEOUT_ULL_MAX / 1000000.0); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
|
Shouldn't this have been cherry-picked for each release? |
|
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. |
|
It looks like multiple things went wrong here.
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. |
No description provided.