From: Juergen Perlinger Date: Thu, 22 Sep 2016 18:42:39 +0000 (+0200) Subject: [Bug 3116] unit tests for NTP time stamp expansion X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=15a3a208522e354932a3c72b255ad32a6f464c1a;p=thirdparty%2Fntp.git [Bug 3116] unit tests for NTP time stamp expansion bk: 57e4261fIZWFN6sN9Wisl5lgZp4Ftw --- diff --git a/ChangeLog b/ChangeLog index 0805467dc..db857f942 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +--- +* [Bug 3116] unit tests for NTP time stamp expansion. + --- (4.2.8p8) 2016/06/02 Released by Harlan Stenn diff --git a/libntp/ntp_calendar.c b/libntp/ntp_calendar.c index ff6ead364..4bfb0e723 100644 --- a/libntp/ntp_calendar.c +++ b/libntp/ntp_calendar.c @@ -91,7 +91,7 @@ /* *--------------------------------------------------------------------- * replacing the 'time()' function - * -------------------------------------------------------------------- + *--------------------------------------------------------------------- */ static systime_func_ptr systime_func = &time; @@ -395,7 +395,7 @@ ntpcal_get_build_date( /* *--------------------------------------------------------------------- * basic calendar stuff - * -------------------------------------------------------------------- + *--------------------------------------------------------------------- */ /* month table for a year starting with March,1st */ @@ -443,11 +443,11 @@ static const uint16_t real_month_table[2][13] = { */ /* - * ================================================================== + * ==================================================================== * * General algorithmic stuff * - * ================================================================== + * ==================================================================== */ /* @@ -495,7 +495,7 @@ static const uint16_t real_month_table[2][13] = { * 32/16bit divisions and is still performant is a bit more * difficult. Since most usecases can be coded in a way that does only * require the 32-bit version a 64bit version is NOT provided here. - * --------------------------------------------------------------------- + *--------------------------------------------------------------------- */ int32_t ntpcal_periodic_extend( @@ -542,8 +542,35 @@ ntpcal_periodic_extend( return pivot; } +/*--------------------------------------------------------------------- + * Note to the casual reader + * + * In the next two functions you will find (or would have found...) + * the expression + * + * res.Q_s -= 0x80000000; + * + * There was some ruckus about a possible programming error due to + * integer overflow and sign propagation. + * + * This assumption is based on a lack of understanding of the C + * standard. (Though this is admittedly not one of the most 'natural' + * aspects of the 'C' language and easily to get wrong.) + * + * see + * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf + * "ISO/IEC 9899:201x Committee Draft — April 12, 2011" + * 6.4.4.1 Integer constants, clause 5 + * + * why there is no sign extension/overflow problem here. + * + * But to ease the minds of the doubtful, I added back the 'u' qualifiers + * that somehow got lost over the last years. + */ + + /* - *------------------------------------------------------------------- + *--------------------------------------------------------------------- * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X * scale with proper epoch unfolding around a given pivot or the current * system time. This function happily accepts negative pivot values as @@ -553,7 +580,7 @@ ntpcal_periodic_extend( * This is also a periodic extension, but since the cycle is 2^32 and * the shift is 2^31, we can do some *very* fast math without explicit * divisions. - *------------------------------------------------------------------- + *--------------------------------------------------------------------- */ vint64 ntpcal_ntp_to_time( @@ -568,7 +595,7 @@ ntpcal_ntp_to_time( res.q_s = (pivot != NULL) ? *pivot : now(); - res.Q_s -= 0x80000000; /* unshift of half range */ + res.Q_s -= 0x80000000u; /* unshift of half range */ ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ ntp -= res.D_s.lo; /* cycle difference */ res.Q_s += (uint64_t)ntp; /* get expanded time */ @@ -581,7 +608,7 @@ ntpcal_ntp_to_time( ? *pivot : now(); res = time_to_vint64(&tmp); - M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000); + M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u); ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ ntp -= res.D_s.lo; /* cycle difference */ M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); @@ -592,7 +619,7 @@ ntpcal_ntp_to_time( } /* - *------------------------------------------------------------------- + *--------------------------------------------------------------------- * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP * scale with proper epoch unfolding around a given pivot or the current * system time. @@ -602,7 +629,7 @@ ntpcal_ntp_to_time( * This is also a periodic extension, but since the cycle is 2^32 and * the shift is 2^31, we can do some *very* fast math without explicit * divisions. - *------------------------------------------------------------------- + *--------------------------------------------------------------------- */ vint64 ntpcal_ntp_to_ntp( @@ -617,7 +644,7 @@ ntpcal_ntp_to_ntp( res.q_s = (pivot) ? *pivot : now(); - res.Q_s -= 0x80000000; /* unshift of half range */ + res.Q_s -= 0x80000000u; /* unshift of half range */ res.Q_s += (uint32_t)JAN_1970; /* warp into NTP domain */ ntp -= res.D_s.lo; /* cycle difference */ res.Q_s += (uint64_t)ntp; /* get expanded time */ @@ -642,20 +669,20 @@ ntpcal_ntp_to_ntp( /* - * ================================================================== + * ==================================================================== * * Splitting values to composite entities * - * ================================================================== + * ==================================================================== */ /* - *------------------------------------------------------------------- + *--------------------------------------------------------------------- * Split a 64bit seconds value into elapsed days in 'res.hi' and * elapsed seconds since midnight in 'res.lo' using explicit floor * division. This function happily accepts negative time values as * timestamps before the respective epoch start. - * ------------------------------------------------------------------- + *--------------------------------------------------------------------- */ ntpcal_split ntpcal_daysplit( @@ -736,11 +763,11 @@ ntpcal_daysplit( } /* - *------------------------------------------------------------------- + *--------------------------------------------------------------------- * Split a 32bit seconds value into h/m/s and excessive days. This * function happily accepts negative time values as timestamps before * midnight. - * ------------------------------------------------------------------- + *--------------------------------------------------------------------- */ static int32_t priv_timesplit( @@ -773,7 +800,7 @@ priv_timesplit( } /* - * --------------------------------------------------------------------- + *--------------------------------------------------------------------- * Given the number of elapsed days in the calendar era, split this * number into the number of elapsed years in 'res.hi' and the number * of elapsed days of that year in 'res.lo'. @@ -1053,11 +1080,11 @@ ntpcal_time_to_date( /* - * ================================================================== + * ==================================================================== * * merging composite entities * - * ================================================================== + * ==================================================================== */ /* @@ -1251,8 +1278,8 @@ ntpcal_edate_to_eradays( * Convert ELAPSED years/months/days of gregorian calendar to elapsed * days in year. * - * Note: This will give the true difference to the start of the given year, - * even if months & days are off-scale. + * Note: This will give the true difference to the start of the given + * year, even if months & days are off-scale. *--------------------------------------------------------------------- */ int32_t @@ -1434,11 +1461,11 @@ ntpcal_date_to_time( /* - * ================================================================== + * ==================================================================== * * extended and unchecked variants of caljulian/caltontp * - * ================================================================== + * ==================================================================== */ int ntpcal_ntp64_to_date( @@ -1500,11 +1527,11 @@ ntpcal_date_to_ntp( /* - * ================================================================== + * ==================================================================== * * day-of-week calculations * - * ================================================================== + * ==================================================================== */ /* * Given a RataDie and a day-of-week, calculate a RDN that is reater-than, @@ -1557,7 +1584,7 @@ ntpcal_weekday_lt( } /* - * ================================================================== + * ==================================================================== * * ISO week-calendar conversions * @@ -1601,7 +1628,7 @@ ntpcal_weekday_lt( * smallest possible powers of two, so the division can be implemented * as shifts if the optimiser chooses to do so. * - * ================================================================== + * ==================================================================== */ /* diff --git a/tests/libntp/calendar.c b/tests/libntp/calendar.c index 9d25c41b1..b631565c0 100644 --- a/tests/libntp/calendar.c +++ b/tests/libntp/calendar.c @@ -2,6 +2,7 @@ #include "ntp_stdlib.h" /* test fail without this include, for some reason */ #include "ntp_calendar.h" +#include "ntp_unixtime.h" #include "unity.h" #include @@ -19,6 +20,7 @@ char * DateFromCalToString(const struct calendar *cal); char * DateFromIsoToString(const struct isodate *iso); int IsEqualDateCal(const struct calendar *expected, const struct calendar *actual); int IsEqualDateIso(const struct isodate *expected, const struct isodate *actual); + void test_DaySplitMerge(void); void test_SplitYearDays1(void); void test_SplitYearDays2(void); @@ -35,6 +37,8 @@ void test_IsoCalWeeksToYearStart(void); void test_IsoCalWeeksToYearEnd(void); void test_DaySecToDate(void); +void test_NtpToNtp(void); +void test_NtpToTime(void); void setUp(void) @@ -608,3 +612,126 @@ test_DaySecToDate(void) return; } + +/* -------------------------------------------------------------------- + * unfolding of (truncated) NTP time stamps to full 64bit values. + * + * Note: These tests need a 64bit time_t to be useful. + */ + +void +test_NtpToNtp(void) +{ +# if SIZEOF_TIME_T <= 4 + + TEST_IGNORE_MESSAGE("test only useful for sizeof(time_t) > 4, skipped"); + +# else + + static const uint32_t ntp_vals[6] = { + UINT32_C(0x00000000), + UINT32_C(0x00000001), + UINT32_C(0x7FFFFFFF), + UINT32_C(0x80000000), + UINT32_C(0x80000001), + UINT32_C(0xFFFFFFFF) + }; + + static char lbuf[128]; + vint64 hold; + time_t pivot, texp, diff; + int loops, iloop; + + pivot = 0; + for (loops = 0; loops < 16; ++loops) { + for (iloop = 0; iloop < 6; ++iloop) { + hold = ntpcal_ntp_to_ntp( + ntp_vals[iloop], &pivot); + texp = vint64_to_time(&hold); + + /* constraint 1: texp must be in the + * (right-open) intervall [p-(2^31), p+(2^31)[, + * but the pivot 'p' must be taken in full NTP + * time scale! + */ + diff = texp - (pivot + JAN_1970); + snprintf(lbuf, sizeof(lbuf), + "bounds check: piv=%lld exp=%lld dif=%lld", + (long long)pivot, + (long long)texp, + (long long)diff); + TEST_ASSERT_MESSAGE((diff >= INT32_MIN) && (diff <= INT32_MAX), + lbuf); + + /* constraint 2: low word must be equal to + * input + */ + snprintf(lbuf, sizeof(lbuf), + "low check: ntp(in)=$%08lu ntp(out[0:31])=$%08lu", + (unsigned long)ntp_vals[iloop], + (unsigned long)hold.D_s.lo); + TEST_ASSERT_EQUAL_MESSAGE(ntp_vals[iloop], hold.D_s.lo, lbuf); + } + pivot += 0x20000000; + } +# endif +} + +void +test_NtpToTime(void) +{ +# if SIZEOF_TIME_T <= 4 + + TEST_IGNORE_MESSAGE("test only useful for sizeof(time_t) > 4, skipped"); + +# else + + static const uint32_t ntp_vals[6] = { + UINT32_C(0x00000000), + UINT32_C(0x00000001), + UINT32_C(0x7FFFFFFF), + UINT32_C(0x80000000), + UINT32_C(0x80000001), + UINT32_C(0xFFFFFFFF) + }; + + static char lbuf[128]; + vint64 hold; + time_t pivot, texp, diff; + uint32_t back; + int loops, iloop; + + pivot = 0; + for (loops = 0; loops < 16; ++loops) { + for (iloop = 0; iloop < 6; ++iloop) { + hold = ntpcal_ntp_to_time( + ntp_vals[iloop], &pivot); + texp = vint64_to_time(&hold); + + /* constraint 1: texp must be in the + * (right-open) intervall [p-(2^31), p+(2^31)[ + */ + diff = texp - pivot; + snprintf(lbuf, sizeof(lbuf), + "bounds check: piv=%lld exp=%lld dif=%lld", + (long long)pivot, + (long long)texp, + (long long)diff); + TEST_ASSERT_MESSAGE((diff >= INT32_MIN) && (diff <= INT32_MAX), + lbuf); + + /* constraint 2: conversion from full time back + * to truncated NTP time must yield same result + * as input. + */ + back = (uint32_t)texp + JAN_1970; + snprintf(lbuf, sizeof(lbuf), + "modulo check: ntp(in)=$%08lu ntp(out)=$%08lu", + (unsigned long)ntp_vals[iloop], + (unsigned long)back); + TEST_ASSERT_EQUAL_MESSAGE(ntp_vals[iloop], back, lbuf); + } + pivot += 0x20000000; + } +# endif +} diff --git a/tests/libntp/run-calendar.c b/tests/libntp/run-calendar.c index 555f3ba6d..82309bd3b 100644 --- a/tests/libntp/run-calendar.c +++ b/tests/libntp/run-calendar.c @@ -25,6 +25,7 @@ #include "config.h" #include "ntp_stdlib.h" #include "ntp_calendar.h" +#include "ntp_unixtime.h" #include //=======External Functions This Runner Calls===== @@ -45,6 +46,8 @@ extern void test_IsoCalYearsToWeeks(void); extern void test_IsoCalWeeksToYearStart(void); extern void test_IsoCalWeeksToYearEnd(void); extern void test_DaySecToDate(void); +extern void test_NtpToNtp(void); +extern void test_NtpToTime(void); //=======Test Reset Option===== @@ -63,21 +66,23 @@ int main(int argc, char *argv[]) { progname = argv[0]; UnityBegin("calendar.c"); - RUN_TEST(test_DaySplitMerge, 22); - RUN_TEST(test_SplitYearDays1, 23); - RUN_TEST(test_SplitYearDays2, 24); - RUN_TEST(test_RataDie1, 25); - RUN_TEST(test_LeapYears1, 26); - RUN_TEST(test_LeapYears2, 27); - RUN_TEST(test_RoundTripDate, 28); - RUN_TEST(test_RoundTripYearStart, 29); - RUN_TEST(test_RoundTripMonthStart, 30); - RUN_TEST(test_RoundTripWeekStart, 31); - RUN_TEST(test_RoundTripDayStart, 32); - RUN_TEST(test_IsoCalYearsToWeeks, 33); - RUN_TEST(test_IsoCalWeeksToYearStart, 34); - RUN_TEST(test_IsoCalWeeksToYearEnd, 35); - RUN_TEST(test_DaySecToDate, 36); + RUN_TEST(test_DaySplitMerge, 24); + RUN_TEST(test_SplitYearDays1, 25); + RUN_TEST(test_SplitYearDays2, 26); + RUN_TEST(test_RataDie1, 27); + RUN_TEST(test_LeapYears1, 28); + RUN_TEST(test_LeapYears2, 29); + RUN_TEST(test_RoundTripDate, 30); + RUN_TEST(test_RoundTripYearStart, 31); + RUN_TEST(test_RoundTripMonthStart, 32); + RUN_TEST(test_RoundTripWeekStart, 33); + RUN_TEST(test_RoundTripDayStart, 34); + RUN_TEST(test_IsoCalYearsToWeeks, 35); + RUN_TEST(test_IsoCalWeeksToYearStart, 36); + RUN_TEST(test_IsoCalWeeksToYearEnd, 37); + RUN_TEST(test_DaySecToDate, 38); + RUN_TEST(test_NtpToNtp, 40); + RUN_TEST(test_NtpToTime, 41); return (UnityEnd()); }