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