This patch makes ctime, localtime, tzset, wcsftime multithread-safe.
2024-02-09 Bruno Haible <br...@clisp.org> ctime, localtime, tzset, wcsftime: Make multithread-safe. * lib/ctime.c: Include <wchar.h>. (rpl_ctime): Modify _environ and _wenviron without using _putenv. * lib/localtime.c: Include <wchar.h>. (rpl_localtime): Modify _environ and _wenviron without using _putenv. * lib/tzset.c: Include <wchar.h>. (rpl_tzset): Modify _environ and _wenviron without using _putenv. * lib/wcsftime.c (rpl_wcsftime): Likewise. 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 <stdlib.h> #include <string.h> +#if defined _WIN32 && ! defined __CYGWIN__ +# include <wchar.h> +#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 <stdlib.h> #include <string.h> +#if defined _WIN32 && ! defined __CYGWIN__ +# include <wchar.h> +#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 <stdlib.h> #include <string.h> +#if defined _WIN32 && ! defined __CYGWIN__ +# include <wchar.h> +#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 <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/posix-tzset> 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);