fix TZ parsing logic for identifying POSIX-form strings
authorRich Felker <dalias@aerifal.cx>
Wed, 23 Jun 2021 21:22:47 +0000 (17:22 -0400)
committerRich Felker <dalias@aerifal.cx>
Wed, 23 Jun 2021 21:30:10 +0000 (17:30 -0400)
previously, the contents of the TZ variable were considered a
candidate for a file/path name only if they began with a colon or
contained a slash before any comma. the latter was very sloppy logic
to avoid treating any valid POSIX TZ string as a file name, but it
also triggered on values that are not valid POSIX TZ strings,
including 3-letter timezone names without any offset.

instead, only treat the TZ variable as POSIX form if it begins with a
nonzero standard time name followed by +, -, or a digit.

also, special case GMT and UTC to always be treated as POSIX form
(with implicit zero offset) so that a stray file by the same name
cannot break software that depends on setting TZ=GMT or TZ=UTC.

src/time/__tz.c

index 09a6317..3e2fcdc 100644 (file)
@@ -4,6 +4,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <ctype.h>
 #include "libc.h"
 #include "lock.h"
 #include "fork_impl.h"
@@ -154,10 +155,21 @@ static void do_tzset()
        }
        if (old_tz) memcpy(old_tz, s, i+1);
 
+       int posix_form = 0;
+       if (*s != ':') {
+               p = s;
+               char dummy_name[TZNAME_MAX+1];
+               getname(dummy_name, &p);
+               if (p!=s && (*p == '+' || *p == '-' || isdigit(*p)
+                            || !strcmp(dummy_name, "UTC")
+                            || !strcmp(dummy_name, "GMT")))
+                       posix_form = 1;
+       }       
+
        /* Non-suid can use an absolute tzfile pathname or a relative
         * pathame beginning with "."; in secure mode, only the
         * standard path will be searched. */
-       if (*s == ':' || ((p=strchr(s, '/')) && !memchr(s, ',', p-s))) {
+       if (!posix_form) {
                if (*s == ':') s++;
                if (*s == '/' || *s == '.') {
                        if (!libc.secure || !strcmp(s, "/etc/localtime"))