BUG: Using portMAX_DELAY with select() results in no delay

xutengl
Posts: 4
Joined: Thu Jun 01, 2023 11:44 pm

BUG: Using portMAX_DELAY with select() results in no delay

Postby xutengl » Thu Mar 13, 2025 12:42 am

Issue
While implementing UDP socket listening on multiple ports, I discovered a bug when trying to make select() wait indefinitely.
I passed portMAX_DELAY/1000 (0xFFFFFFFF/1000) as the tv_sec value in struct timeval, expecting it to wait for a very long time. Instead, the function returned immediately without waiting.

Root Cause
Looking at the LwIP implementation of select(), I found that internally it converts the timeout to a signed long:

Code: Select all

long msecs_long = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500) / 1000));
On ESP32, long is implemented as a 32-bit integer (int32_t). When using large values like portMAX_DELAY, this calculation overflows the 32-bit signed integer limit and becomes negative, causing LwIP to set a minimal timeout (1ms) instead of a maximum one.
The maximum allowed timeout value is 0x7FFFFFFF milliseconds (approximately 24.85 days), which is INT32_MAX.

Solution
To make select() wait indefinitely, pass NULL as the timeout parameter:

Code: Select all

// This will wait forever
int ret = select(sock + 1, &rfds, NULL, NULL, NULL);
Suggestions for Improvement
Add explicit documentation to the select() function noting the valid timeout range and recommending NULL for indefinite waits.
Modify the implementation to gracefully handle the case where tv_sec is too large, either by:
  • Clamping large values to the maximum safe timeout
  • Converting indefinite-looking timeouts (like UINT32_MAX) to NULL automatically
This would prevent subtle timing bugs in applications that use common FreeRTOS patterns like portMAX_DELAY with socket APIs.

MicroController
Posts: 2669
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: BUG: Using portMAX_DELAY with select() results in no delay

Postby MicroController » Thu Mar 13, 2025 2:50 pm

  • Converting indefinite-looking timeouts (like UINT32_MAX) to NULL automatically
This would prevent subtle timing bugs in applications that use common FreeRTOS patterns like portMAX_DELAY with socket APIs.
For (POSIX) select(), there is no "indefinite-looking" timeval; every value passed in implies a finite timeout period and select() may return 0. NULL is the only way defined to indicate "infinite"/no timeout.

Sprite
Espressif staff
Espressif staff
Posts: 10612
Joined: Thu Nov 26, 2015 4:08 am

Re: BUG: Using portMAX_DELAY with select() results in no delay

Postby Sprite » Fri Mar 14, 2025 12:09 am

But to be fair, you don't expect a time_t (which from what I can see is an uint32_t) to be cast to an int32_t and then effectively ignored as it turns out to be negative.

MicroController
Posts: 2669
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: BUG: Using portMAX_DELAY with select() results in no delay

Postby MicroController » Fri Mar 14, 2025 10:23 am

You're right. That's why I figure implementing the "clamp-to-max" approach is the better solution, compared to a "pretty long = infinite" interpretation.

(Though I'm inclined to argue that passing a timeout value of 3 weeks+ is actually an 'error' on the developer's side because he/she actually wanted to pass NULL = infinite.)

Who is online

Users browsing this forum: No registered users and 4 guests