Conversation
e336026 to
3683b3a
Compare
|
This is a non trivial issue and I'm investigating a fix as well. |
3683b3a to
d791eaf
Compare
d791eaf to
d3169f4
Compare
The original localtime/1 implementation had several correctness issues: - Memory leak on newlib/picolibc (ESP32): setenv leaks the old "NAME=value" string on every overwrite (espressif/esp-idf#3046). Fix: use putenv with a fixed-size static buffer that is installed once and modified in place, avoiding repeated allocations. Guarded behind __NEWLIB__ / __PICOLIBC__ so standard platforms use normal setenv/unsetenv. - Use-after-free: the getenv("TZ") pointer was used after setenv overwrites it, reading freed or stale memory. Fix: strdup the old TZ value before modifying the environment. - Missing tzset after restore: the original code called tzset only when setting the new TZ, not after restoring the old one, leaving libc in an inconsistent state for subsequent calls. - Missing localtime_r NULL check: a failing localtime_r would pass NULL to build_datetime_from_tm. Fix: raise badarg on NULL. The static buffer workaround raises badarg for oversized TZ strings rather than silently leaking. The restore path falls back to setenv if the buffer is too small (refreshing the cached env pointer to maintain the static buffer invariant), since the environment must always be restored. When TZ was originally unset, "UTC0" is used as the restore value on newlib/picolibc since putenv cannot remove entries; this is a pragmatic approximation (not a true restore) that is acceptable because unset TZ defaults to UTC on these platforms. The concurrency model assumes AtomVM is the sole user of TZ -- no external threads should read or write TZ or call time functions that depend on it during the temporary override. See: espressif/esp-idf#3046 Signed-off-by: Peter M <petermm@gmail.com>
ca8904b to
bb6d834
Compare
https://pubs.opengroup.org/onlinepubs/009695399/functions/putenv.html Couldn't we do putenv+unsetenv? |
|
According to Codex, no:
|
|
Looking at the source code of putenv in picolibc, it is not POSIX compliant. The passed strings doesn't seem to be part of the environment from my (sapiens) understanding. It calls setenv and the str is not passed at all to setenv. So this doesn't fix the issue with picolibc. Can we simply swap environ while holding the lock? It works on macOS at least and seems dirty but compliant. It may work with both newlib and picolibc. There is no guarantee that localtime (or localtime_r) doesn't read TZ by the standard, but picolibc doesn't. |
Fixes #2062
replicate:
And see rapidly decreasing free memory - solution inspired by espressif/esp-idf#9764
Currently in draft, as I believe there is some issue with setting the tz to "" on generic_unix - which means it is set to utc, just need to understand that or guard it..
These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).
SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later