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