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