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
Days(int Year)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
main(void)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