|
|
Message-ID: <488f8d57-555b-9328-f768-5779ea8aa8ec@gmail.com>
Date: Fri, 9 Jun 2017 17:15:40 +1000
From: Patrick Oppenlander <pattyo.lists@...il.com>
To: musl@...ts.openwall.com
Subject: detect timezone changes by monitoring /etc/localtime (like glibc)
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 <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
#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);
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.