Created attachment 6368 [details] test case Originally discovered as a thread-santizer reported race against glibc-2.11. Reproduced in current git trunk. People generally assume that localtime_r is thread-safe, and does not update tzname[]. In __tz_convert() we call tzset_internal() with always==0 to avoid writing tzname, only to reset tzname in __tzfile_compute() which we call immediately afterward. Attached test case shows: ./a.out 2 __tzname[0] = 'PST' __tzname[1] = 'PDT' a.out: localtime_r_race.c:21: fn: Assertion `tzname_copy[0] != ((void *)0)' failed. Aborted GDB shows: __tzname[0] = 'PST' __tzname[1] = 'PDT' [New Thread 0x7ffff7a12700 (LWP 26081)] [New Thread 0x7ffff7211700 (LWP 26082)] a.out: localtime_r_race.c:21: fn: Assertion `tzname_copy[0] != ((void *)0)' failed. Program received signal SIGABRT, Aborted. [Switching to Thread 0x7ffff7a12700 (LWP 26081)] 0x00007ffff7a49f9b in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:66 66 int res = INLINE_SYSCALL (tgkill, 3, pid, selftid, sig); (gdb) thread apply all where Thread 3 (Thread 0x7ffff7211700 (LWP 26082)): #0 __strcmp_sse2 () at ../sysdeps/x86_64/multiarch/../strcmp.S:2257 #1 0x00007ffff7abc797 in __tzstring (s=0x7ffff8201954 "PDT") at tzset.c:101 #2 0x00007ffff7abe682 in __tzfile_compute (timer=140737356241236, use_localtime=use_localtime@entry=1, leap_correct=leap_correct@entry=0x7ffff7210e48, leap_hit=leap_hit@entry=0x7ffff7210e44, tp=tp@entry=0x7ffff7210e80) at tzfile.c:756 #3 0x00007ffff7abd399 in __tz_convert (timer=0x7ffff7210ed8, use_localtime=1, tp=0x7ffff7210e80) at tzset.c:626 #4 0x00000000004008e2 in fn (p=0x0) at localtime_r_race.c:17 #5 0x00007ffff7dc5fab in start_thread (arg=0x7ffff7211700) at pthread_create.c:304 #6 0x00007ffff7afb99d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:114 Thread 2 (Thread 0x7ffff7a12700 (LWP 26081)): #0 0x00007ffff7a49f9b in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:66 #1 0x00007ffff7a4b458 in __GI_abort () at abort.c:90 #2 0x00007ffff7a42ff2 in __assert_fail_base (fmt=0x7ffff7b7b628 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=assertion@entry=0x400b93 "tzname_copy[0] != ((void *)0)", file=file@entry=0x400b80 "localtime_r_race.c", line=line@entry=21, function=function@entry=0x400c25 "fn") at assert.c:93 #3 0x00007ffff7a430a2 in __GI___assert_fail (assertion=0x400b93 "tzname_copy[0] != ((void *)0)", file=0x400b80 "localtime_r_race.c", line=21, function=0x400c25 "fn") at assert.c:102 #4 0x000000000040091a in fn (p=0x0) at localtime_r_race.c:21 #5 0x00007ffff7dc5fab in start_thread (arg=0x7ffff7a12700) at pthread_create.c:304 #6 0x00007ffff7afb99d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:114 Thread 1 (Thread 0x7ffff7a14700 (LWP 26080)): #0 0x00007ffff7dc722f in pthread_join (threadid=140737347921664, thread_return=0x0) at pthread_join.c:88 #1 0x0000000000400a73 in main (argc=2, argv=0x7fffffffd630) at localtime_r_race.c:61 #2 0x00007ffff7a36705 in __libc_start_main (main=0x40093c <main>, argc=2, ubp_av=0x7fffffffd630, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffd628) at libc-start.c:225 #3 0x00000000004007f9 in _start ()
POSIX explicitly allows but does not require this behavior: "Unlike localtime(), the localtime_r() function is not required to set tzname. If localtime_r() does not set tzname, it shall not set daylight and shall not set timezone." Source: http://pubs.opengroup.org/onlinepubs/9699919799/functions/localtime.html
(In reply to comment #1) > POSIX explicitly allows but does not require this behavior: Yes, I've read that description before filing the bug. I believe it's a quality-of-implementation issue: effectively one can't use tzname at all in a glibc multi-threaded program, even if one calls only "thread-safe" localtime_r. This behaviour is very likely to surprise most developers.
Here patch would be straigthforward, but would need some work. Paul could you take care of it? First could we add additional argument to __tzfile_compute to suppress writing timezone? If not then we need rename it to __tzfile_compute_internal as wrapper. We would add extra argument local_only Then we need add local variable like tzname_local, rename __tzname references and write to __tzname when local_only is not set.
(In reply to Ondrej Bilka from comment #3) > Here patch would be straigthforward, but would need some work. Paul could > you take care of it? I'll prepare a patch.
*** Bug 19179 has been marked as a duplicate of this bug. ***
19179 is the same problem with gmtime_r