#4338 closed defect (fixed)
rtems_clock_set(): Cannot set future dates later than approximately 2105
Reported by: | Frank Kuehndel | Owned by: | Sebastian Huber |
---|---|---|---|
Priority: | normal | Milestone: | 6.1 |
Component: | rtems | Version: | 6 |
Severity: | normal | Keywords: | rtems_clock_set, 2514, _TOD_To_seconds, qualification |
Cc: | Blocked By: | ||
Blocking: |
Description
Short Problem Description
RTEMS Classic API Guide says:
RTEMS can represent time points of this clock in nanoseconds ranging from 1988-01-01T00:00:00.000000000Z to 2514-05-31T01:53:03.999999999Z.
- Yet, years larger than roughly 2105 to 2110 cannot be set (or at least the date should be wrong but this never occured in my tests).
The possible RETURN VALUES are RTEMS_SUCCESSFUL, RTEMS_INVALID_ADDRESS, RTEMS_INVALID_CLOCK
- Yet, rtems_clock_set() can return status RTEMS_INVALID_NUMBER, too. (Only for such dates in the far future.)
How To Replicate?
Call rtems_clock_set() with this time_of-day:
{
year = 2514,
month = 5,
day = 31,
hour = 1,
minute = 53,
second = 3,
ticks = 995
}
The return value will be RTEMS_INVALID_NUMBER.
Bugs in _TOD_To_seconds()
cpukit/rtems/src/clockset.c: rtems_clock_set() calls
- cpukit/rtems/src/clocktodvalidate.c: _TOD_Validate() and
- cpukit/rtems/src/clocktodtoseconds.c: _TOD_To_seconds() and
- cpukit/score/src/coretodset.c: _TOD_Set()
First issue:
_TOD_To_seconds() converts the time_of_day structure into seconds using a variable time
of type uint32_t
. This simply overflows when in comes close to year 2110.
Debugger output at the end of _TOD_To_seconds():
(gdb) print the_tod->year $16 = 2104 (gdb) print time $17 = 4233686400
with a higher year:
(gdb) print the_tod->year $28 = 2514 (gdb) print *the_tod $31 = { year = 2514, month = 5, day = 31, hour = 1, minute = 53, second = 3, ticks = 995 } (gdb) print time $29 = 192272
Second issue:
_TOD_To_seconds() can (most likely) not handle the leap year issues of the years 2200, 2300, 2400, 2500 because it has dedicated code for the 2100 case only:
/* The year 2100 is not a leap year */ if ( time >= (TOD_SECONDS_AT_2100_03_01_00_00 - TOD_SECONDS_1970_THROUGH_1988)) { time -= TOD_SECONDS_PER_DAY; }
_TOD_Check_time_of_day_and_run_hooks() causes STATUS_INVALID_NUMBER
cpukit/score/src/coretodset.c: _TOD_Set() calls
- cpukit/score/src/coretodset.c: _TOD_Check_time_of_day_and_run_hooks()
in this code snippet (note return status
):
status = _TOD_Check_time_of_day_and_run_hooks( tod ); if ( status != STATUS_SUCCESSFUL ) { _TOD_Release( lock_context ); return status; }
_TOD_Check_time_of_day_and_run_hooks( tod ) can return status STATUS_INVALID_NUMBER which is not documented for rtems_clock_set(). The small time in seconds value 192272 from the integer overrun discussed above triggers the middle if
clause:
static Status_Control _TOD_Check_time_of_day_and_run_hooks( const struct timespec *tod ) { if ( !_Watchdog_Is_valid_timespec( tod ) ) { return STATUS_INVALID_NUMBER; } if ( tod->tv_sec < TOD_SECONDS_1970_THROUGH_1988 ) { return STATUS_INVALID_NUMBER; } if ( _Watchdog_Is_far_future_timespec( tod ) ) { return STATUS_INVALID_NUMBER; } return _TOD_Hook_Run( TOD_ACTION_SET_CLOCK, tod ); }
Final Notes
- I found #2665 and #2548 but I do not say these are relevant here.
#define WATCHDOG_MAX_SECONDS 0x3ffffffff
in cpukit/include/rtems/score/watchdogimpl.h covers 17179869183 seconds which are some 544 years- _TOD_Check_time_of_day_and_run_hooks() seems to be only used by _TOD_Set() but _TOD_Set() has three users.
- I did not check whether the invers conversation (rtems_clock_get_tod()) works for dates which are centuries in the future.
Change History (11)
comment:1 Changed on 03/12/21 at 14:02:46 by Joel Sherrill
comment:2 Changed on 03/12/21 at 15:30:38 by Frank Kuehndel
time_t
- I presume 64 bit - will avoid the overflow in _TOD_To_seconds() but- ... there are still the not correctly handled far away leap years.
- ... there is STATUS_INVALID_NUMBER (Should not occur when the overflow is fixed. Yet, I do not like such incorrect code even if it should not trigger.)
- Overruns in rtems_clock_get_ticks_since_boot() are OK because the Classic API Guide says:
With a 1ms clock tick, this counter overflows after 50 days since boot. This is the historical measure of uptime in an RTEMS system. The newer service rtems_clock_get_uptime() is another and potentially more accurate way of obtaining similar information.
- At other functions like rtems_clock_get_seconds_since_epoch() I had not have a look, yet.
comment:3 Changed on 03/12/21 at 15:51:58 by Joel Sherrill
OK. I was unclear but I was asking if it is time to move rtems_interval to 64-bits. This would address the historical ticks since boot and other intervals being 32 bits. The limit also applied to rtems_task_wake_after() and other APIs using rtems_interval for number of ticks.
comment:4 Changed on 03/12/21 at 16:25:14 by Sebastian Huber
Could we please separate the 32-bit rtems_interval issues from this ticket.
If we need a 64-bit division we should think about limiting the time of day to the year 2104. If it is just a multiplication and a couple of leap year fixes, then we should keep the full watchdog range.
comment:6 Changed on 05/06/21 at 13:20:56 by Frank Kuehndel
Update on status:
This is fixed in code. What is missing is to update the documentation (i.e. removing the 2514-05-31T01:53:03.999999999Z in favor of 2104-12-31T23:59:59.999999999Z in Classic API and may be explaining that the internal clock will run beyond this limit and POSIX API can work with dates beyond 2105). A colleague of mine promised to do that.
comment:7 Changed on 06/18/21 at 09:24:45 by Sebastian Huber
Keywords: | qualification added |
---|
comment:9 Changed on 09/06/21 at 10:27:18 by Sebastian Huber <sebastian.huber@…>
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
comment:10 Changed on 09/06/21 at 10:40:03 by Sebastian Huber <sebastian.huber@…>
In c0435b5e/rtems:
Will switching to time_t instead of uint32_t largely resolve this?
I think you might have missed some Classic API methods which might overflow. I think anything using uint32_t or rtems_interval related to seconds or ticks over long intervals is suspect. I spotted these:
I think the Classic API time of day structure is OK.