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