1 /* check_y2k.c -- test ntp code constructs for Y2K correctness Y2KFixes [*/ 2 3 /* 4 Code invoked by `make check`. Not part of ntpd and not to be 5 installed. 6 7 On any code I even wonder about, I've cut and pasted the code 8 here and ran it as a test case just to be sure. 9 10 For code not in "ntpd" proper, we have tried to call most 11 repaired functions from herein to properly test them 12 (something never done before!). This has found several bugs, 13 not normal Y2K bugs, that will strike in Y2K so repair them 14 we did. 15 16 Program exits with 0 on success, 1 on Y2K failure (stdout messages). 17 Exit of 2 indicates internal logic bug detected OR failure of 18 what should be our correct formulas. 19 20 While "make check" should only check logic for source within that 21 specific directory, this check goes outside the scope of the local 22 directory. It's not a perfect world (besides, there is a lot of 23 interdependence here, and it really needs to be tested in 24 a controled order). 25 */ 26 27 /* { definitions lifted from ntpd.c to allow us to complie with 28 "#include ntp.h". I have not taken the time to reduce the clutter. */ 29 30 #ifdef HAVE_CONFIG_H 31 # include <config.h> 32 #endif 33 34 #include "ntpd.h" 35 36 #ifdef HAVE_UNISTD_H 37 # include <unistd.h> 38 #endif 39 #ifdef HAVE_SYS_STAT_H 40 # include <sys/stat.h> 41 #endif 42 #include <stdio.h> 43 #include <errno.h> 44 #ifndef SYS_WINNT 45 # if !defined(VMS) /*wjm*/ 46 # include <sys/param.h> 47 # endif /* VMS */ 48 # if HAVE_SYS_SIGNAL_H 49 # include <sys/signal.h> 50 # endif /* HAVE_SYS_SIGNAL_H */ 51 # include <sys/signal.h> 52 # ifdef HAVE_SYS_IOCTL_H 53 # include <sys/ioctl.h> 54 # endif /* HAVE_SYS_IOCTL_H */ 55 # if !defined(VMS) /*wjm*/ 56 # include <sys/resource.h> 57 # endif /* VMS */ 58 #else 59 # include <signal.h> 60 # include <process.h> 61 # include <io.h> 62 # include "../libntp/log.h" 63 #endif /* SYS_WINNT */ 64 #if defined(HAVE_RTPRIO) 65 # ifdef HAVE_SYS_RESOURCE_H 66 # include <sys/resource.h> 67 # endif 68 # ifdef HAVE_SYS_LOCK_H 69 # include <sys/lock.h> 70 # endif 71 # include <sys/rtprio.h> 72 #else 73 # ifdef HAVE_PLOCK 74 # ifdef HAVE_SYS_LOCK_H 75 # include <sys/lock.h> 76 # endif 77 # endif 78 #endif 79 #if defined(HAVE_SCHED_SETSCHEDULER) 80 # ifdef HAVE_SCHED_H 81 # include <sched.h> 82 # else 83 # ifdef HAVE_SYS_SCHED_H 84 # include <sys/sched.h> 85 # endif 86 # endif 87 #endif 88 #if defined(HAVE_SYS_MMAN_H) 89 # include <sys/mman.h> 90 #endif 91 92 #ifdef HAVE_TERMIOS_H 93 # include <termios.h> 94 #endif 95 96 #ifdef SYS_DOMAINOS 97 # include <apollo/base.h> 98 #endif /* SYS_DOMAINOS */ 99 100 /* } end definitions lifted from ntpd.c */ 101 102 #include "ntp_calendar.h" 103 #include "parse.h" 104 105 #define GoodLeap(Year) (((Year)%4 || (!((Year)%100) && (Year)%400)) ? 0 : 13 ) 106 107 volatile int debug = 0; /* debugging requests for parse stuff */ 108 char const *progname = "check_y2k"; 109 110 long 111 Days ( int Year ) /* return number of days since year "0" */ 112 { 113 long Return; 114 /* this is a known to be good algorithm */ 115 Return = Year * 365; /* first aproximation to the value */ 116 if ( Year >= 1 ) 117 { /* see notes in libparse/parse.c if you want a PROPER 118 * **generic algorithm. */ 119 Return += (Year+3) / 4; /* add in (too many) leap days */ 120 Return -= (Year-1) / 100; /* reduce by (too many) centurys */ 121 Return += (Year-1) / 400; /* get final answer */ 122 } 123 124 return Return; 125 } 126 127 static int year0 = 1900; /* sarting year for NTP time */ 128 static int yearend; /* ending year we test for NTP time. 129 * 32-bit systems: through 2036, the 130 **year in which NTP time overflows. 131 * 64-bit systems: a reasonable upper 132 **limit (well, maybe somewhat beyond 133 **reasonable, but well before the 134 **max time, by which time the earth 135 **will be dead.) */ 136 static time_t Time; 137 static struct tm LocalTime; 138 139 #define Error(year) if ( (year)>=2036 && LocalTime.tm_year < 110 ) \ 140 Warnings++; else Fatals++ 141 142 int 143 main( void ) 144 { 145 int Fatals; 146 int Warnings; 147 int year; 148 149 Time = time( (time_t *)NULL ) 150 #ifdef TESTTIMEOFFSET 151 + test_time_offset 152 #endif 153 ; 154 LocalTime = *localtime( &Time ); 155 156 year = ( sizeof( u_long ) > 4 ) /* save max span using year as temp */ 157 ? ( 400 * 3 ) /* three greater gregorian cycles */ 158 : ((int)(0x7FFFFFFF / 365.242 / 24/60/60)* 2 ); /*32-bit limit*/ 159 /* NOTE: will automacially expand test years on 160 * 64 bit machines.... this may cause some of the 161 * existing ntp logic to fail for years beyond 162 * 2036 (the current 32-bit limit). If all checks 163 * fail ONLY beyond year 2036 you may ignore such 164 * errors, at least for a decade or so. */ 165 yearend = year0 + year; 166 167 puts( " internal self check" ); 168 { /* verify our own logic used to verify repairs */ 169 unsigned long days; 170 171 if ( year0 >= yearend ) 172 { 173 fprintf( stdout, "year0=%d NOT LESS THAN yearend=%d (span=%d)\n", 174 (int)year0, (int)yearend, (int)year ); 175 exit(2); 176 } 177 178 { 179 int save_year; 180 181 save_year = LocalTime.tm_year; /* save current year */ 182 183 year = 1980; 184 LocalTime.tm_year = year - 1900; 185 Fatals = Warnings = 0; 186 Error(year); /* should increment Fatals */ 187 if ( Fatals == 0 ) 188 { 189 fprintf( stdout, 190 "%4d: %s(%d): FATAL DID NOT INCREMENT (Fatals=%d Warnings=%d)\n", 191 (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings ); 192 exit(2); 193 } 194 195 year = 2100; /* test year > limit but CURRENT year < limit */ 196 Fatals = Warnings = 0; 197 Error(year); /* should increment Fatals */ 198 if ( Warnings == 0 ) 199 { 200 fprintf( stdout, 201 "%4d: %s(%d): WARNING DID NOT INCREMENT (Fatals=%d Warnings=%d)\n", 202 (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings ); 203 exit(2); 204 } 205 Fatals = Warnings = 0; 206 LocalTime.tm_year = year - 1900; /* everything > limit */ 207 Error(1980); /* should increment Fatals */ 208 if ( Fatals == 0 ) 209 { 210 fprintf( stdout, 211 "%4d: %s(%d): FATALS DID NOT INCREMENT (Fatals=%d Warnings=%d)\n", 212 (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings ); 213 exit(2); 214 } 215 216 LocalTime.tm_year = save_year; 217 } 218 219 days = 365+1; /* days in year 0 + 1 more day */ 220 for ( year = 1; year <= 2500; year++ ) 221 { 222 long Test; 223 Test = Days( year ); 224 if ( days != Test ) 225 { 226 fprintf( stdout, "%04d: Days() DAY COUNT ERROR: s/b=%ld was=%ld\n", 227 year, (long)days, (long)Test ); 228 exit(2); /* would throw off many other tests */ 229 } 230 231 Test = julian0(year); /* compare with julian0() macro */ 232 if ( days != Test ) 233 { 234 fprintf( stdout, "%04d: julian0() DAY COUNT ERROR: s/b=%ld was=%ld\n", 235 year, (long)days, (long)Test ); 236 exit(2); /* would throw off many other tests */ 237 } 238 239 days += 365; 240 if ( isleap_4(year) ) days++; 241 } 242 243 if ( isleap_4(1999) ) 244 { 245 fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" ); 246 exit(2); 247 } 248 if ( !isleap_4(2000) ) 249 { 250 fprintf( stdout, "isleap_4(2000) REPORTED FALSE\n" ); 251 exit(2); 252 } 253 if ( isleap_4(2001) ) 254 { 255 fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" ); 256 exit(2); 257 } 258 259 if ( !isleap_tm(2000-1900) ) 260 { 261 fprintf( stdout, "isleap_tm(100) REPORTED FALSE\n" ); 262 exit(2); 263 } 264 } 265 266 Fatals = Warnings = 0; 267 268 puts( " include/ntp.h" ); 269 { /* test our new isleap_*() #define "functions" */ 270 271 for ( year = 1400; year <= 2200; year++ ) 272 { 273 int LeapSw; 274 int IsLeapSw; 275 276 LeapSw = GoodLeap(year); 277 IsLeapSw = isleap_4(year); 278 279 if ( !!LeapSw != !!IsLeapSw ) 280 { 281 Error(year); 282 fprintf( stdout, 283 " %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw ); 284 break; 285 } 286 287 IsLeapSw = isleap_tm(year-1900); 288 289 if ( !!LeapSw != !!IsLeapSw ) 290 { 291 Error(year); 292 fprintf( stdout, 293 " %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw ); 294 break; 295 } 296 } 297 } 298 299 puts( " include/ntp_calendar.h" ); 300 { /* I belive this is good, but just to be sure... */ 301 302 /* we are testing this #define */ 303 #define is_leapyear(y) (y%4 == 0 && !(y%100 == 0 && !(y%400 == 0))) 304 305 for ( year = 1400; year <= 2200; year++ ) 306 { 307 int LeapSw; 308 309 LeapSw = GoodLeap(year); 310 311 if ( !(!LeapSw) != !(!is_leapyear(year)) ) 312 { 313 Error(year); 314 fprintf( stdout, 315 " %4d %2d *** ERROR\n", year, LeapSw ); 316 break; 317 } 318 } 319 } 320 321 322 puts( " libparse/parse.c" ); 323 { 324 long Days1970; /* days from 1900 to 1970 */ 325 326 struct ParseTime /* womp up a test structure to all cut/paste code */ 327 { 328 int year; 329 } Clock_Time, *clock_time; 330 331 clock_time = &Clock_Time; 332 333 /* first test this #define */ 334 #define days_per_year(x) ((x) % 4 ? 365 : ((x % 400) ? ((x % 100) ? 366 : 365) : 366)) 335 336 for ( year = 1400; year <= 2200; year++ ) 337 { 338 int LeapSw; 339 int DayCnt; 340 341 LeapSw = GoodLeap(year); 342 DayCnt = (int)days_per_year(year); 343 344 if ( ( LeapSw ? 366 : 365 ) != DayCnt ) 345 { 346 Error(year); 347 fprintf( stdout, 348 " days_per_year() %4d %2d %3d *** ERROR\n", 349 year, LeapSw, DayCnt ); 350 break; 351 } 352 } 353 354 /* test (what is now julian0) calculations */ 355 356 Days1970 = Days( 1970 ); /* get days since 1970 using a known good */ 357 358 for ( year = 1970; year < yearend; year++ ) 359 { 360 unsigned long t; 361 long DaysYear ; 362 363 clock_time->year = year; 364 365 /* here is the code we are testing, cut and pasted out of the source */ 366 #if 0 /* old BUGGY code that has Y2K (and many other) failures */ 367 /* ghealton: this logic FAILED with great frequency when run 368 * over a period of time, including for year 2000. True, it 369 * had more successes than failures, but that's not really good 370 * enough for critical time distribution software. 371 * It is so awful I wonder if it has had a history of failure 372 * and fixes? */ 373 t = (clock_time->year - 1970) * 365; 374 t += (clock_time->year >> 2) - (1970 >> 2); 375 t -= clock_time->year / 100 - 1970 / 100; 376 t += clock_time->year / 400 - 1970 / 400; 377 378 /* (immediate feare of rounding errors on integer 379 * **divisions proved well founded) */ 380 381 #else 382 /* my replacement, based on Days() above */ 383 t = julian0(year) - julian0(1970); 384 #endif 385 386 /* compare result in t against trusted calculations */ 387 DaysYear = Days( year ); /* get days to this year */ 388 if ( t != DaysYear - Days1970 ) 389 { 390 Error(year); 391 fprintf( stdout, 392 " %4d 1970=%-8ld %4d=%-8ld %-3ld t=%-8ld *** ERROR ***\n", 393 year, (long)Days1970, 394 year, 395 (long)DaysYear, 396 (long)(DaysYear - Days1970), 397 (long)t ); 398 } 399 } 400 401 #if 1 /* { */ 402 { 403 debug = 1; /* enable debugging */ 404 for ( year = 1970; year < yearend; year++ ) 405 { /* (limited by theory unix 2038 related bug lives by, but 406 * ends in yearend) */ 407 clocktime_t ct; 408 time_t Observed; 409 time_t Expected; 410 u_long Flag; 411 unsigned long t; 412 413 ct.day = 1; 414 ct.month = 1; 415 ct.year = year; 416 ct.hour = ct.minute = ct.second = ct.usecond = 0; 417 ct.utcoffset = 0; 418 ct.utctime = 0; 419 ct.flags = 0; 420 421 Flag = 0; 422 Observed = parse_to_unixtime( &ct, &Flag ); 423 if ( ct.year != year ) 424 { 425 fprintf( stdout, 426 "%04d: parse_to_unixtime(,%d) CORRUPTED ct.year: was %d\n", 427 (int)year, (int)Flag, (int)ct.year ); 428 Error(year); 429 break; 430 } 431 t = julian0(year) - julian0(1970); /* Julian day from 1970 */ 432 Expected = t * 24 * 60 * 60; 433 if ( Observed != Expected || Flag ) 434 { /* time difference */ 435 fprintf( stdout, 436 "%04d: parse_to_unixtime(,%d) FAILURE: was=%lu s/b=%lu (%ld)\n", 437 year, (int)Flag, 438 (unsigned long)Observed, (unsigned long)Expected, 439 ((long)Observed - (long)Expected) ); 440 Error(year); 441 break; 442 } 443 444 if ( year >= YEAR_PIVOT+1900 ) 445 { 446 /* check year % 100 code we put into parse_to_unixtime() */ 447 ct.utctime = 0; 448 ct.year = year % 100; 449 Flag = 0; 450 451 Observed = parse_to_unixtime( &ct, &Flag ); 452 453 if ( Observed != Expected || Flag ) 454 { /* time difference */ 455 fprintf( stdout, 456 "%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu (%ld)\n", 457 year, (int)ct.year, (int)Flag, 458 (unsigned long)Observed, (unsigned long)Expected, 459 ((long)Observed - (long)Expected) ); 460 Error(year); 461 break; 462 } 463 464 /* check year - 1900 code we put into parse_to_unixtime() */ 465 ct.utctime = 0; 466 ct.year = year - 1900; 467 Flag = 0; 468 469 Observed = parse_to_unixtime( &ct, &Flag ); 470 471 if ( Observed != Expected || Flag ) 472 { /* time difference */ 473 fprintf( stdout, 474 "%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu (%ld)\n", 475 year, (int)ct.year, (int)Flag, 476 (unsigned long)Observed, (unsigned long)Expected, 477 ((long)Observed - (long)Expected) ); 478 Error(year); 479 break; 480 } 481 482 483 } 484 } 485 #endif /* } */ 486 } 487 } 488 489 puts( " libntp/caljulian.c" ); 490 { /* test caljulian() */ 491 struct calendar ot; 492 u_long ntp_time; /* NTP time */ 493 494 year = year0; /* calculate the basic year */ 495 printf( " starting year %04d\n", (int)year0 ); 496 printf( " ending year %04d\n", (int)yearend ); 497 498 499 ntp_time = julian0( year0 ); /* NTP starts in 1900-01-01 */ 500 #if DAY_NTP_STARTS == 693596 501 ntp_time -= 365; /* BIAS required for successful test */ 502 #endif 503 if ( DAY_NTP_STARTS != ntp_time ) 504 { 505 Error(year); 506 fprintf( stdout, 507 "%04d: DAY_NTP_STARTS (%ld) NOT TRUE VALUE OF %ld (%ld)\n", 508 (int)year0, 509 (long)DAY_NTP_STARTS, (long)ntp_time, 510 (long)DAY_NTP_STARTS - (long)ntp_time ); 511 } 512 513 for ( ; year < yearend; year++ ) 514 { 515 516 /* 01-01 for the current year */ 517 ntp_time = Days( year ) - Days( year0 ); /* days into NTP time */ 518 ntp_time *= 24 * 60 * 60; /* convert into seconds */ 519 caljulian( ntp_time, &ot ); /* convert January 1 */ 520 if ( ot.year != year 521 || ot.month != 1 522 || ot.monthday != 1 ) 523 { 524 Error(year); 525 fprintf( stdout, "%lu: EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n", 526 (unsigned long)ntp_time, 527 year, 528 (int)ot.year, (int)ot.month, (int)ot.monthday ); 529 break; 530 } 531 532 ntp_time += (31 + 28-1) * ( 24 * 60 * 60 ); /* advance to 02-28 */ 533 caljulian( ntp_time, &ot ); /* convert Feb 28 */ 534 if ( ot.year != year 535 || ot.month != 2 536 || ot.monthday != 28 ) 537 { 538 Error(year); 539 fprintf( stdout, "%lu: EXPECTED %04d-02-28: FOUND %04d-%02d-%02d\n", 540 (unsigned long)ntp_time, 541 year, 542 (int)ot.year, (int)ot.month, (int)ot.monthday ); 543 break; 544 } 545 546 { 547 int m; /* expected month */ 548 int d; /* expected day */ 549 550 m = isleap_4(year) ? 2 : 3; 551 d = isleap_4(year) ? 29 : 1; 552 553 ntp_time += ( 24 * 60 * 60 ); /* advance to the next day */ 554 caljulian( ntp_time, &ot ); /* convert this day */ 555 if ( ot.year != year 556 || ot.month != m 557 || ot.monthday != d ) 558 { 559 Error(year); 560 fprintf( stdout, "%lu: EXPECTED %04d-%02d-%02d: FOUND %04d-%02d-%02d\n", 561 (unsigned long)ntp_time, 562 year, m, d, 563 (int)ot.year, (int)ot.month, (int)ot.monthday ); 564 break; 565 } 566 567 } 568 } 569 } 570 571 puts( " libntp/caltontp.c" ); 572 { /* test caltontp() */ 573 struct calendar ot; 574 u_long ntp_time; /* NTP time */ 575 576 year = year0; /* calculate the basic year */ 577 printf( " starting year %04d\n", (int)year0 ); 578 printf( " ending year %04d\n", (int)yearend ); 579 580 581 for ( ; year < yearend; year++ ) 582 { 583 u_long ObservedNtp; 584 585 /* 01-01 for the current year */ 586 ot.year = year; 587 ot.month = ot.monthday = 1; /* unused, but set anyway JIC */ 588 ot.yearday = 1; /* this is the magic value used by caltontp() */ 589 ot.hour = ot.minute = ot.second = 0; 590 591 ntp_time = Days( year ) - Days( year0 ); /* days into NTP time */ 592 ntp_time *= 24 * 60 * 60; /* convert into seconds */ 593 ObservedNtp = caltontp( &ot ); 594 if ( ntp_time != ObservedNtp ) 595 { 596 Error(year); 597 fprintf( stdout, "%d: EXPECTED %lu: FOUND %lu (%ld)\n", 598 (int)year, 599 (unsigned long)ntp_time, (unsigned long)ObservedNtp , 600 (long)ntp_time - (long)ObservedNtp ); 601 602 break; 603 } 604 605 /* now call caljulian as a type of failsafe supercheck */ 606 caljulian( ObservedNtp, &ot ); /* convert January 1 */ 607 if ( ot.year != year 608 || ot.month != 1 609 || ot.monthday != 1 ) 610 { 611 Error(year); 612 fprintf( stdout, "%lu: caljulian FAILSAFE EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n", 613 (unsigned long)ObservedNtp, 614 year, 615 (int)ot.year, (int)ot.month, (int)ot.monthday ); 616 break; 617 } 618 } 619 } 620 621 if ( Warnings > 0 ) 622 fprintf( stdout, "%d WARNINGS\n", Warnings ); 623 if ( Fatals > 0 ) 624 fprintf( stdout, "%d FATAL ERRORS\n", Fatals ); 625 return Fatals ? 1 : 0; 626 } 627 /* Y2KFixes ] */ 628