From: Frank Kühndel <frank.kuehn...@embedded-brains.de> This patch fixes issue #4338 by changing _TOD_Validate() to only accept years till 2105. This requires another patch to change the documentation of rtems_clock_set() and other affected API functions (indicating the end date is 2105 not 2514).
I tried to support till year 2514 but it turned out that this needs changing the Timer Manager too. That in turn would mean to change _TOD_Seconds_since_epoch( void ) from 32 to 64 bit. Sebastian pointed out that a naive extension leads to trouble with 32 bit processors. He deemed a safe re-implementation too costly performance wise considering that year 2106 is far away and current binaries using RTEMS Classic API are unlikely to be in use by 2106. The constant TOD_SECONDS_AT_2100_03_01_00_00 in cpukit/rtems/src/clocktodtoseconds.c happens to be wrong by 1 hour. When setting the date 2100-Feb-28 23:59:59 and then reading the date again you will find yourself in 2100-Feb-27. Original Bug #4338 Text: == Short Problem Description == [https://docs.rtems.org/branches/master/c-user/clock/directives.html > 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. Update #4338 --- cpukit/include/rtems/score/todimpl.h | 16 ++++++++++++++++ cpukit/rtems/src/clocktodtoseconds.c | 2 +- cpukit/rtems/src/clocktodvalidate.c | 1 + testsuites/sptests/sp2038/init.c | 28 ++++++++++++++++++---------- 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/cpukit/include/rtems/score/todimpl.h b/cpukit/include/rtems/score/todimpl.h index 9805ec0dfd..316a56ec74 100644 --- a/cpukit/include/rtems/score/todimpl.h +++ b/cpukit/include/rtems/score/todimpl.h @@ -123,6 +123,22 @@ extern "C" { */ #define TOD_BASE_YEAR 1988 +/** + * @brief Latest year to which a time of day can be initialized. + * + * The following constant defines the latest year to which an + * RTEMS time of day can be set using rtems_clock_set(). + * + * 32 bits can accept as latest point in time 2106-Feb-7 6:28:15 + * but to simplify the implementation, is was decided to only + * check that the year is not greater than the year of this constant. + * + * The internal realtime clock can run centuries longer but in + * contrast to the POSIX API, the RTEMS Classic API does not + * support this for efficiency reasons. + */ +#define TOD_LATEST_YEAR 2105 + /** * @addtogroup RTEMSScoreTOD * diff --git a/cpukit/rtems/src/clocktodtoseconds.c b/cpukit/rtems/src/clocktodtoseconds.c index 49ae257243..86e89f86eb 100644 --- a/cpukit/rtems/src/clocktodtoseconds.c +++ b/cpukit/rtems/src/clocktodtoseconds.c @@ -23,7 +23,7 @@ #include <rtems/rtems/clockimpl.h> #include <rtems/score/todimpl.h> -#define TOD_SECONDS_AT_2100_03_01_00_00 4107538800UL +#define TOD_SECONDS_AT_2100_03_01_00_00 4107542400UL /* * The following array contains the number of days in all months diff --git a/cpukit/rtems/src/clocktodvalidate.c b/cpukit/rtems/src/clocktodvalidate.c index d8af275d04..2685bfd6e7 100644 --- a/cpukit/rtems/src/clocktodvalidate.c +++ b/cpukit/rtems/src/clocktodvalidate.c @@ -52,6 +52,7 @@ bool _TOD_Validate( (the_tod->month == 0) || (the_tod->month > TOD_MONTHS_PER_YEAR) || (the_tod->year < TOD_BASE_YEAR) || + (the_tod->year > TOD_LATEST_YEAR) || (the_tod->day == 0) ) return false; diff --git a/testsuites/sptests/sp2038/init.c b/testsuites/sptests/sp2038/init.c index a3f09d4156..10850d9c4d 100644 --- a/testsuites/sptests/sp2038/init.c +++ b/testsuites/sptests/sp2038/init.c @@ -149,8 +149,7 @@ static const uint32_t sample_seconds [] = { 4168736895UL, 4200272895UL, 4231808895UL, - 4263431295UL, - 4294967295UL + 4263431295UL }; static const rtems_time_of_day nearly_problem_2038 = { @@ -171,8 +170,8 @@ static const rtems_time_of_day problem_2038 = { .second = 8 }; -static const rtems_time_of_day nearly_problem_2106 = { - .year = 2106, +static const rtems_time_of_day tod_to_seconds_base = { + .year = 0, .month = 2, .day = 7, .hour = 6, @@ -180,13 +179,22 @@ static const rtems_time_of_day nearly_problem_2106 = { .second = 15 }; +static const rtems_time_of_day nearly_problem_2106 = { + .year = 2105, + .month = 12, + .day = 31, + .hour = 23, + .minute = 59, + .second = 59 +}; + static const rtems_time_of_day problem_2106 = { .year = 2106, - .month = 2, - .day = 7, - .hour = 6, - .minute = 28, - .second = 16 + .month = 1, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0 }; static const rtems_time_of_day problem_2100 = { @@ -214,7 +222,7 @@ static void test_tod_to_seconds(void) size_t n = sizeof(sample_seconds) / sizeof(sample_seconds [0]); for (i = 0; i < n; ++i) { - rtems_time_of_day tod = nearly_problem_2106; + rtems_time_of_day tod = tod_to_seconds_base; uint32_t seconds = 0; rtems_interval seconds_as_interval = 0; -- 2.26.2 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel