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