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