xref: /netbsd-src/external/bsd/ntp/dist/ntpd/check_y2k.c (revision cdfa2a7ef92791ba9db70a584a1d904730e6fb46)
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