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