The attached test program (time_test.c) enters an infinite loop when
run under macOS 10.6.
(compiled with: gcc -g3 time_test.c -o time_test.bin
run with: ./time_test.bin)
The localtime_r call has this backtrace:
(gdb) bt
#0 0x00007fff83860ad3 in timesub () from /usr/lib/libSystem.B.dylib
#1 0x00007fff8386081c in _st_localsub () from /usr/lib/libSystem.B.dylib
#2 0x00007fff83869d71 in localtime_r () from /usr/lib/libSystem.B.dylib
#3 0x0000000100000f19 in main () at time_test.c:6
The gmtime_r call (commented) has this backtrace:
(gdb) bt
#0 0x00007fff83860b24 in timesub () from /usr/lib/libSystem.B.dylib
#1 0x00007fff838929cc in gmtsub () from /usr/lib/libSystem.B.dylib
#2 0x0000000100000f19 in main () at time_test.c:7
I think it's a bug in macOS' handling of large time values.
(gdb) p /t large_time
$3 = 1111111100001111001111010101010000000000000000000000000000000000
The same bug happens when gnulib calls gmtime_r or localtime_r (as in
localtime_rz of time_rz.c). I originally reported the bug (it
happened while building Emacs, which tracks gnulib) here:
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27706
And it was also reported a second time here:
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27736
I've written a makeshift program (attached; time_find.c) to show
roughly which values of time_t cause the issue. It seems to be a
range of values around time_t = -67768038400720896 (roughly
year -2**31, I think.) The output is here:
lib car313$ ./time_find.bin
No hang for -67768038400770896
No hang for -67768038400760896
Hang detected for -67768038400750896
Hang detected for -67768038400740896
Hang detected for -67768038400730896
Hang detected for -67768038400720896
Hang detected for -67768038400710896
Hang detected for -67768038400700896
Hang detected for -67768038400690896
Hang detected for -67768038400680896
Hang detected for -67768038400670896
No hang for -67768038400660896
No hang for -67768038400650896
Could we put a workaround for these "bad" values of time_t in gnulib
for this version of macOS?
Thanks in advance.
#include "time.h" /* gnulib time.h */
int main() {
time_t large_time = -67768038400720896;
struct tm output_time;
localtime_r(&large_time, &output_time);
/* gmtime_r(&large_time, &output_time); */
return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "time.h" /* gnulib time.h */
#include <setjmp.h>
#define TIME_CHECK_DELTA 10000
#define TIME_CHECK_RANGE 120000
static jmp_buf buf;
static time_t orig_large_time = -67768038400770896;
static time_t large_time = -67768038400770896;
/* time_find.c */
int localtime_hang_handler () {
printf ("Hang detected for %ld\n", (long) large_time);
longjmp(buf, 1);
}
int main () {
struct tm output_time;
signal (SIGALRM, (void *) localtime_hang_handler);
if(setjmp(buf))
next:
large_time = large_time + TIME_CHECK_DELTA;
alarm(1);
localtime_r (&large_time, &output_time);
printf ("No hang for %ld\n", (long) large_time);
alarm(0);
if (large_time < (orig_large_time + TIME_CHECK_RANGE)) {
goto next;
}
return 0;
}