diff --git a/components/esp_libc/src/time.c b/components/esp_libc/src/time.c index 991d58dc550..17513b1692f 100644 --- a/components/esp_libc/src/time.c +++ b/components/esp_libc/src/time.c @@ -189,8 +189,15 @@ WEAK_UNLESS_TIMEFUNC_IMPL int adjtime(const struct timeval *delta, struct timeva struct timex tx = {0}; if (delta != NULL) { + // Reject deltas that do not fit in the 32-bit struct timex.offset (µs) field, + // otherwise the value would be silently truncated and pass the range check. + int64_t offset_us = (int64_t)delta->tv_sec * 1000000LL + delta->tv_usec; + if (offset_us > LONG_MAX || offset_us < LONG_MIN) { + errno = EINVAL; + return -1; + } tx.modes = ADJ_OFFSET_SINGLESHOT; - tx.offset = delta->tv_sec * 1000000L + delta->tv_usec; + tx.offset = (long)offset_us; } else { tx.modes = ADJ_OFFSET_SS_READ; } diff --git a/components/esp_libc/test_apps/newlib/main/test_time.c b/components/esp_libc/test_apps/newlib/main/test_time.c index 5051ed2a6ef..6a94a2ab461 100644 --- a/components/esp_libc/test_apps/newlib/main/test_time.c +++ b/components/esp_libc/test_apps/newlib/main/test_time.c @@ -192,6 +192,18 @@ void test_adjtime_function(test_adjtime_mode_t mode, test_clock_adjtime_units_t TEST_ASSERT_EQUAL(realtime_adjtime_wrapper(&tv_delta, &tv_outdelta, mode, units), -1); } + // a multi-day delta must be rejected, not silently + // truncated into the 32-bit timex.offset (µs) field and applied as a small slew. + if (mode == TEST_ADJTIME_MODE_LEGACY) { + tv_delta.tv_sec = 400L * 24 * 60 * 60; // 400 days + tv_delta.tv_usec = 123456; + TEST_ASSERT_EQUAL(realtime_adjtime_wrapper(&tv_delta, NULL, mode, units), -1); + + tv_delta.tv_sec = -400L * 24 * 60 * 60; + tv_delta.tv_usec = -123456; + TEST_ASSERT_EQUAL(realtime_adjtime_wrapper(&tv_delta, NULL, mode, units), -1); + } + tv_delta.tv_sec = 0; tv_delta.tv_usec = -900000; TEST_ASSERT_EQUAL(realtime_adjtime_wrapper(&tv_delta, &tv_outdelta, mode, units), 0);