From mboxrd@z Thu Jan 1 00:00:00 1970 X-Msuck: nntp://news.gmane.org/gmane.linux.lib.musl.general/11411 Path: news.gmane.org!.POSTED!not-for-mail From: Patrick Oppenlander Newsgroups: gmane.linux.lib.musl.general Subject: detect timezone changes by monitoring /etc/localtime (like glibc) Date: Fri, 9 Jun 2017 17:15:40 +1000 Message-ID: <488f8d57-555b-9328-f768-5779ea8aa8ec@gmail.com> Reply-To: musl@lists.openwall.com NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit X-Trace: blaine.gmane.org 1496992559 25699 195.159.176.226 (9 Jun 2017 07:15:59 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Fri, 9 Jun 2017 07:15:59 +0000 (UTC) User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.1.1 To: musl@lists.openwall.com Original-X-From: musl-return-11424-gllmg-musl=m.gmane.org@lists.openwall.com Fri Jun 09 09:15:55 2017 Return-path: Envelope-to: gllmg-musl@m.gmane.org Original-Received: from mother.openwall.net ([195.42.179.200]) by blaine.gmane.org with smtp (Exim 4.84_2) (envelope-from ) id 1dJE9C-0006Px-IG for gllmg-musl@m.gmane.org; Fri, 09 Jun 2017 09:15:54 +0200 Original-Received: (qmail 28334 invoked by uid 550); 9 Jun 2017 07:15:57 -0000 Mailing-List: contact musl-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Original-Received: (qmail 28272 invoked from network); 9 Jun 2017 07:15:55 -0000 X-Virus-Scanned: amavisd-new at motec.com.au DKIM-Filter: OpenDKIM Filter v2.11.0 camber.motec.com.au 23D32177259 Content-Language: en-AU Xref: news.gmane.org gmane.linux.lib.musl.general:11411 Archived-At: During some recent testing I came across a bug when adjusting timezones on an embedded system by changing /etc/localtime. The cause ended up being a behavioural difference between glibc and musl. A program compiled against musl will not detect changes to /etc/localtime, while a program compiled against glibc will. Intuitively it seems like correct behaviour to me to detect and act upon system timezone changes, however I am not familiar with any pertinent specifications for this. Under glibc if you set TZ=:/etc/localtime changes will not be detected and no extra syscalls are invoked. This is equivalent to the current musl behaviour. Given the above I have attached a proposed patch for review. This is compile-tested only as I ran out of time and would like reviewer comments before going any further. Is a change in this direction acceptable? One possible (unlikely?) problem could be if TZ changes in such a way as to match the cached stat values. Patrick ===8<=== diff --git a/src/time/__tz.c b/src/time/__tz.c index ffe8d402..305616d5 100644 --- a/src/time/__tz.c +++ b/src/time/__tz.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "libc.h" long __timezone = 0; @@ -17,15 +18,24 @@ static char std_name[TZNAME_MAX+1]; static char dst_name[TZNAME_MAX+1]; const char __gmt[] = "GMT"; +static const char etc_lt[] = "/etc/localtime"; + static int dst_off; static int r0[5], r1[5]; static const unsigned char *zi, *trans, *index, *types, *abbrevs, *abbrevs_end; static size_t map_size; -static char old_tz_buf[32]; -static char *old_tz = old_tz_buf; -static size_t old_tz_size = sizeof old_tz_buf; +static union { + char tz_buf[32]; + struct { + ino_t ino; + dev_t dev; + time_t mtime; + }; +} old; +static char *old_tz = old.tz_buf; +static size_t old_tz_size = sizeof old.tz_buf; static volatile int lock[2]; @@ -123,27 +133,37 @@ static void do_tzset() size_t i; static const char search[] = "/usr/share/zoneinfo/\0/share/zoneinfo/\0/etc/zoneinfo/\0"; + struct stat st; s = getenv("TZ"); - if (!s) s = "/etc/localtime"; + if (!s) s = stat(etc_lt, &st) == 0 ? etc_lt : __gmt; if (!*s) s = __gmt; - if (old_tz && !strcmp(s, old_tz)) return; + if (s == etc_lt && st.st_ino == old.ino && st.st_dev == old.dev && + st.st_mtime == old.mtime) return; + if (s != etc_lt && old_tz && !strcmp(s, old_tz)) return; if (zi) __munmap((void *)zi, map_size); - /* Cache the old value of TZ to check if it has changed. Avoid - * free so as not to pull it into static programs. Growth - * strategy makes it so free would have minimal benefit anyway. */ - i = strlen(s); - if (i > PATH_MAX+1) s = __gmt, i = 3; - if (i >= old_tz_size) { - old_tz_size *= 2; - if (i >= old_tz_size) old_tz_size = i+1; - if (old_tz_size > PATH_MAX+2) old_tz_size = PATH_MAX+2; - old_tz = malloc(old_tz_size); + /* Cache the old value of TZ or stat of /etc/localtime to check + * if it has changed. Avoid free so as not to pull it into static + * programs. Growth strategy makes it so free would have minimal + * benefit anyway. */ + if (s == etc_lt) { + old.ino = st.st_ino; + old.dev = st.st_dev; + old.mtime = st.st_mtime; + } else { + i = strlen(s); + if (i > PATH_MAX+1) s = __gmt, i = 3; + if (i >= old_tz_size) { + old_tz_size *= 2; + if (i >= old_tz_size) old_tz_size = i+1; + if (old_tz_size > PATH_MAX+2) old_tz_size = PATH_MAX+2; + old_tz = malloc(old_tz_size); + } + if (old_tz) memcpy(old_tz, s, i+1); } - if (old_tz) memcpy(old_tz, s, i+1); /* Non-suid can use an absolute tzfile pathname or a relative * pathame beginning with "."; in secure mode, only the @@ -151,7 +171,7 @@ static void do_tzset() if (*s == ':' || ((p=strchr(s, '/')) && !memchr(s, ',', p-s))) { if (*s == ':') s++; if (*s == '/' || *s == '.') { - if (!libc.secure || !strcmp(s, "/etc/localtime")) + if (!libc.secure || !strcmp(s, etc_lt)) map = __map_file(s, &map_size); } else { size_t l = strlen(s);