From acb16d6339f25238dc422358d93143f6cf413fb9 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Fri, 9 Feb 2024 20:42:53 +0100 Subject: [PATCH] ctime, localtime, tzset, wcsftime: Make multithread-safe. * lib/ctime.c: Include . (rpl_ctime): Modify _environ and _wenviron without using _putenv. * lib/localtime.c: Include . (rpl_localtime): Modify _environ and _wenviron without using _putenv. * lib/tzset.c: Include . (rpl_tzset): Modify _environ and _wenviron without using _putenv. * lib/wcsftime.c (rpl_wcsftime): Likewise. --- ChangeLog | 11 +++++++++++ lib/ctime.c | 20 +++++++++++++++++++- lib/localtime.c | 20 +++++++++++++++++++- lib/tzset.c | 20 +++++++++++++++++++- lib/wcsftime.c | 17 ++++++++++++++++- 5 files changed, 84 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6d72ea28b1..7ada620262 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2024-02-09 Bruno Haible + + ctime, localtime, tzset, wcsftime: Make multithread-safe. + * lib/ctime.c: Include . + (rpl_ctime): Modify _environ and _wenviron without using _putenv. + * lib/localtime.c: Include . + (rpl_localtime): Modify _environ and _wenviron without using _putenv. + * lib/tzset.c: Include . + (rpl_tzset): Modify _environ and _wenviron without using _putenv. + * lib/wcsftime.c (rpl_wcsftime): Likewise. + 2024-02-09 Bruno Haible strftime: Remove module, deprecated in 2017. diff --git a/lib/ctime.c b/lib/ctime.c index a11adc5c74..8c54ef463c 100644 --- a/lib/ctime.c +++ b/lib/ctime.c @@ -21,6 +21,9 @@ #include #include +#if defined _WIN32 && ! defined __CYGWIN__ +# include +#endif #undef ctime @@ -52,7 +55,22 @@ rpl_ctime (const time_t *tp) responsibility. */ const char *tz = getenv ("TZ"); if (tz != NULL && strchr (tz, '/') != NULL) - _putenv ("TZ="); + { + /* Neutralize it, in a way that is multithread-safe. + (If we were to use _putenv ("TZ="), it would free the memory allocated + for the environment variable "TZ", and thus other threads that are + using the previously fetched value of getenv ("TZ") could crash.) */ + char **env = _environ; + wchar_t **wenv = _wenviron; + if (env != NULL) + for (char *s = env; *s != NULL; s++) + if (s[0] == 'T' && s[1] == 'Z' && s[2] == '=') + s[0] = '$'; + if (wenv != NULL) + for (wchar_t *ws = wenv; *ws != NULL; ws++) + if (ws[0] == L'T' && ws[1] == L'Z' && ws[2] == L'=') + ws[0] = L'$'; + } #endif return ctime (tp); diff --git a/lib/localtime.c b/lib/localtime.c index bdea1cab10..f0e91ac647 100644 --- a/lib/localtime.c +++ b/lib/localtime.c @@ -21,6 +21,9 @@ #include #include +#if defined _WIN32 && ! defined __CYGWIN__ +# include +#endif #undef localtime @@ -52,7 +55,22 @@ rpl_localtime (const time_t *tp) responsibility. */ const char *tz = getenv ("TZ"); if (tz != NULL && strchr (tz, '/') != NULL) - _putenv ("TZ="); + { + /* Neutralize it, in a way that is multithread-safe. + (If we were to use _putenv ("TZ="), it would free the memory allocated + for the environment variable "TZ", and thus other threads that are + using the previously fetched value of getenv ("TZ") could crash.) */ + char **env = _environ; + wchar_t **wenv = _wenviron; + if (env != NULL) + for (char *s = env; *s != NULL; s++) + if (s[0] == 'T' && s[1] == 'Z' && s[2] == '=') + s[0] = '$'; + if (wenv != NULL) + for (wchar_t *ws = wenv; *ws != NULL; ws++) + if (ws[0] == L'T' && ws[1] == L'Z' && ws[2] == L'=') + ws[0] = L'$'; + } #endif return localtime (tp); diff --git a/lib/tzset.c b/lib/tzset.c index 0eb8c161f4..f307f0c3d1 100644 --- a/lib/tzset.c +++ b/lib/tzset.c @@ -24,6 +24,9 @@ #include #include +#if defined _WIN32 && ! defined __CYGWIN__ +# include +#endif void rpl_tzset (void) @@ -54,7 +57,22 @@ rpl_tzset (void) responsibility. */ const char *tz = getenv ("TZ"); if (tz != NULL && strchr (tz, '/') != NULL) - _putenv ("TZ="); + { + /* Neutralize it, in a way that is multithread-safe. + (If we were to use _putenv ("TZ="), it would free the memory allocated + for the environment variable "TZ", and thus other threads that are + using the previously fetched value of getenv ("TZ") could crash.) */ + char **env = _environ; + wchar_t **wenv = _wenviron; + if (env != NULL) + for (char *s = env; *s != NULL; s++) + if (s[0] == 'T' && s[1] == 'Z' && s[2] == '=') + s[0] = '$'; + if (wenv != NULL) + for (wchar_t *ws = wenv; *ws != NULL; ws++) + if (ws[0] == L'T' && ws[1] == L'Z' && ws[2] == L'=') + ws[0] = L'$'; + } /* On native Windows, tzset() is deprecated. Use _tzset() instead. See diff --git a/lib/wcsftime.c b/lib/wcsftime.c index 93e0470dd3..d8b471ab57 100644 --- a/lib/wcsftime.c +++ b/lib/wcsftime.c @@ -53,7 +53,22 @@ rpl_wcsftime (wchar_t *buf, size_t bufsize, const wchar_t *format, const struct responsibility. */ const char *tz = getenv ("TZ"); if (tz != NULL && strchr (tz, '/') != NULL) - _putenv ("TZ="); + { + /* Neutralize it, in a way that is multithread-safe. + (If we were to use _putenv ("TZ="), it would free the memory allocated + for the environment variable "TZ", and thus other threads that are + using the previously fetched value of getenv ("TZ") could crash.) */ + char **env = _environ; + wchar_t **wenv = _wenviron; + if (env != NULL) + for (char *s = env; *s != NULL; s++) + if (s[0] == 'T' && s[1] == 'Z' && s[2] == '=') + s[0] = '$'; + if (wenv != NULL) + for (wchar_t *ws = wenv; *ws != NULL; ws++) + if (ws[0] == L'T' && ws[1] == L'Z' && ws[2] == L'=') + ws[0] = L'$'; + } #endif return wcsftime (buf, bufsize, format, tp); -- 2.39.5