@@ -1728,6 +1728,28 @@ term nif_erlang_universaltime_0(Context *ctx, int argc, term argv[])
17281728 return build_datetime_from_tm (ctx , gmtime_r (& ts .tv_sec , & broken_down_time ));
17291729}
17301730
1731+ // Workaround for newlib setenv memory leak: use putenv with a fixed-size
1732+ // static buffer. The buffer is installed once and then modified in place.
1733+ // See: https://github.com/espressif/esp-idf/issues/3046
1734+ #define TZ_BUFFER_SIZE 64
1735+
1736+ static char tz_buffer [TZ_BUFFER_SIZE ] = "TZ=" ;
1737+ static bool tz_buffer_installed = false;
1738+
1739+ static void set_tz_value (const char * tz )
1740+ {
1741+ if (!tz_buffer_installed ) {
1742+ putenv (tz_buffer );
1743+ tz_buffer_installed = true;
1744+ }
1745+ size_t tz_len = strlen (tz );
1746+ if (tz_len > TZ_BUFFER_SIZE - 4 ) {
1747+ tz_len = TZ_BUFFER_SIZE - 4 ;
1748+ }
1749+ memcpy (tz_buffer + 3 , tz , tz_len );
1750+ tz_buffer [3 + tz_len ] = '\0' ;
1751+ }
1752+
17311753term nif_erlang_localtime (Context * ctx , int argc , term argv [])
17321754{
17331755 char * tz ;
@@ -1751,17 +1773,22 @@ term nif_erlang_localtime(Context *ctx, int argc, term argv[])
17511773 smp_spinlock_lock (& ctx -> global -> env_spinlock );
17521774#endif
17531775 if (tz ) {
1754- char * oldtz = getenv ("TZ" );
1755- setenv ("TZ" , tz , 1 );
1776+ char * oldtz = NULL ;
1777+ char * oldtz_env = getenv ("TZ" );
1778+ if (oldtz_env ) {
1779+ oldtz = strdup (oldtz_env );
1780+ }
1781+ set_tz_value (tz );
17561782 tzset ();
17571783 localtime = localtime_r (& ts .tv_sec , & storage );
17581784 if (oldtz ) {
1759- setenv ("TZ" , oldtz , 1 );
1785+ set_tz_value (oldtz );
1786+ free (oldtz );
17601787 } else {
1761- unsetenv ( "TZ " );
1788+ set_tz_value ( " " );
17621789 }
1790+ tzset ();
17631791 } else {
1764- // Call tzset to handle DST changes
17651792 tzset ();
17661793 localtime = localtime_r (& ts .tv_sec , & storage );
17671794 }
0 commit comments