master hovercats/oakiss / pkg / musl / patch / 0007-fix-incorrect-access-to-tzname-by-strptime-Z-convers.patch
  1From 8515ae00b04224597977c4d0ff06636435daa116 Mon Sep 17 00:00:00 2001
  2From: Rich Felker <dalias@aerifal.cx>
  3Date: Sun, 22 Mar 2026 21:32:35 -0400
  4Subject: [PATCH] fix incorrect access to tzname[] by strptime %Z conversion
  5 specifier
  6
  7there are three issues here:
  8
  91. if tzset has not been called (explicitly or implicitly), the
 10tzname[] array will contain null pointers, and the dereference to
 11compare against them has undefined behavior (and will fault).
 12
 132. access to tzname[] was performed without the timezone lock held.
 14this resulted in a data race if the timezone is concurrently changed
 15from another thread.
 16
 173. due to unintended signedness of the types, the open-coded isalpha
 18in the non-matching case was wrong and would continue past null
 19termination.
 20
 21to fix the first two issues, the body of the %Z conversion is moved to
 22__tz.c where it has access to locking, and null checks are added.
 23
 24there is probably an argument to be made that the equivalent of tzset
 25should happen here, but POSIX does not specify that to happen, so in
 26the absence of an interpretation adding such an allowance or
 27requirement, it is not done.
 28
 29the third issue is fixed just by using the existing isalpha macro.
 30---
 31 src/time/__tz.c      | 19 +++++++++++++++++++
 32 src/time/strptime.c  | 13 +++----------
 33 src/time/time_impl.h |  1 +
 34 3 files changed, 23 insertions(+), 10 deletions(-)
 35
 36diff --git a/src/time/__tz.c b/src/time/__tz.c
 37index 54ed4cf6..cfce268e 100644
 38--- a/src/time/__tz.c
 39+++ b/src/time/__tz.c
 40@@ -436,3 +436,22 @@ const char *__tm_to_tzname(const struct tm *tm)
 41 	UNLOCK(lock);
 42 	return p;
 43 }
 44+
 45+int __tzname_to_isdst(const char *restrict *s)
 46+{
 47+	size_t len;
 48+	int isdst = -1;
 49+	LOCK(lock);
 50+	if (tzname[0] && !strncmp(*s, tzname[0], len = strlen(tzname[0]))) {
 51+		isdst = 0;
 52+		*s += len;
 53+	} else if (tzname[1] && !strncmp(*s, tzname[1], len=strlen(tzname[1]))) {
 54+		isdst = 1;
 55+		*s += len;
 56+	} else {
 57+		/* FIXME: is this supposed to be an error? */
 58+		while (isalpha(**s)) ++*s;
 59+	}
 60+	UNLOCK(lock);
 61+	return isdst;
 62+}
 63diff --git a/src/time/strptime.c b/src/time/strptime.c
 64index b1147242..40bb37af 100644
 65--- a/src/time/strptime.c
 66+++ b/src/time/strptime.c
 67@@ -5,6 +5,7 @@
 68 #include <stddef.h>
 69 #include <string.h>
 70 #include <strings.h>
 71+#include "time_impl.h"
 72 
 73 char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm)
 74 {
 75@@ -207,16 +208,8 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri
 76 			s += 5;
 77 			break;
 78 		case 'Z':
 79-			if (!strncmp(s, tzname[0], len = strlen(tzname[0]))) {
 80-				tm->tm_isdst = 0;
 81-				s += len;
 82-			} else if (!strncmp(s, tzname[1], len=strlen(tzname[1]))) {
 83-				tm->tm_isdst = 1;
 84-				s += len;
 85-			} else {
 86-				/* FIXME: is this supposed to be an error? */
 87-				while ((*s|32)-'a' <= 'z'-'a') s++;
 88-			}
 89+			i = __tzname_to_isdst(&s);
 90+			if (i>=0) tm->tm_isdst = i;
 91 			break;
 92 		case '%':
 93 			if (*s++ != '%') return 0;
 94diff --git a/src/time/time_impl.h b/src/time/time_impl.h
 95index f26d8005..ffe5050b 100644
 96--- a/src/time/time_impl.h
 97+++ b/src/time/time_impl.h
 98@@ -5,6 +5,7 @@ hidden int __month_to_secs(int, int);
 99 hidden long long __year_to_secs(long long, int *);
100 hidden long long __tm_to_secs(const struct tm *);
101 hidden const char *__tm_to_tzname(const struct tm *);
102+hidden int __tzname_to_isdst(const char *restrict *);
103 hidden int __secs_to_tm(long long, struct tm *);
104 hidden void __secs_to_zone(long long, int, int *, long *, long *, const char **);
105 hidden const char *__strftime_fmt_1(char (*)[100], size_t *, int, const struct tm *, locale_t, int);
106-- 
1072.49.0
108