@@ -1728,6 +1728,40 @@ 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+
1735+ #define TZ_BUFFER_SIZE 64
1736+
1737+ static char tz_buffer [TZ_BUFFER_SIZE ] = "TZ=" ;
1738+ static char tz_value_buffer [TZ_BUFFER_SIZE - 3 ];
1739+ static bool tz_buffer_installed = false;
1740+ static bool tz_buffer_in_place = false;
1741+
1742+ static void set_tz_value (const char * tz )
1743+ {
1744+ if (!tz_buffer_installed ) {
1745+ putenv (tz_buffer );
1746+ tz_buffer_installed = true;
1747+ char * env_tz = getenv ("TZ" );
1748+ tz_buffer_in_place = (env_tz == tz_buffer + 3 );
1749+ }
1750+ size_t tz_len = strlen (tz );
1751+ if (tz_len > TZ_BUFFER_SIZE - 4 ) {
1752+ tz_len = TZ_BUFFER_SIZE - 4 ;
1753+ }
1754+ if (tz_buffer_in_place ) {
1755+ memcpy (tz_buffer + 3 , tz , tz_len );
1756+ tz_buffer [3 + tz_len ] = '\0' ;
1757+ } else {
1758+ memcpy (tz_value_buffer , tz , tz_len );
1759+ memset (tz_value_buffer + tz_len , ' ' , (TZ_BUFFER_SIZE - 4 ) - tz_len );
1760+ tz_value_buffer [TZ_BUFFER_SIZE - 4 ] = '\0' ;
1761+ setenv ("TZ" , tz_value_buffer , 1 );
1762+ }
1763+ }
1764+
17311765term nif_erlang_localtime (Context * ctx , int argc , term argv [])
17321766{
17331767 char * tz ;
@@ -1751,17 +1785,22 @@ term nif_erlang_localtime(Context *ctx, int argc, term argv[])
17511785 smp_spinlock_lock (& ctx -> global -> env_spinlock );
17521786#endif
17531787 if (tz ) {
1754- char * oldtz = getenv ("TZ" );
1755- setenv ("TZ" , tz , 1 );
1788+ char * oldtz = NULL ;
1789+ char * oldtz_env = getenv ("TZ" );
1790+ if (oldtz_env ) {
1791+ oldtz = strdup (oldtz_env );
1792+ }
1793+ set_tz_value (tz );
17561794 tzset ();
17571795 localtime = localtime_r (& ts .tv_sec , & storage );
17581796 if (oldtz ) {
1759- setenv ("TZ" , oldtz , 1 );
1797+ set_tz_value (oldtz );
1798+ free (oldtz );
17601799 } else {
1761- unsetenv ( "TZ " );
1800+ set_tz_value ( " " );
17621801 }
1802+ tzset ();
17631803 } else {
1764- // Call tzset to handle DST changes
17651804 tzset ();
17661805 localtime = localtime_r (& ts .tv_sec , & storage );
17671806 }
0 commit comments