@@ -159,8 +159,13 @@ pseudo_get_value(const char *key) {
/* Check if the environment has it and we don't ...
* if so, something went wrong... so we'll attempt to recover
+ * -- but not while pseudo_init_util() is running. Its own lookups
+ * below reach this point, and when a value could not be stored
+ * (strdup() failed) re-running it fails the same way and recurses
+ * until the stack overflows.
*/
- if (pseudo_env[i].key && !pseudo_env[i].value && GETENV(pseudo_env[i].key))
+ if (pseudo_util_initted == 0 && pseudo_env[i].key &&
+ !pseudo_env[i].value && GETENV(pseudo_env[i].key))
pseudo_init_util();
if (pseudo_env[i].value)
@@ -218,8 +223,6 @@ pseudo_init_util(void) {
pseudo_set_value(pseudo_env[i].key, GETENV(pseudo_env[i].key));
}
- pseudo_util_initted = 0;
-
/* Somewhere we have to set the debug level.. */
env = pseudo_get_value("PSEUDO_DEBUG");
if (env) {
@@ -241,6 +244,8 @@ pseudo_init_util(void) {
pseudo_evlog_flags_finalize();
}
free(env);
+
+ pseudo_util_initted = 0;
}
unsigned long pseudo_util_debug_flags = 0;
pseudo_get_value() falls back to calling pseudo_init_util() again when a cached value is empty but the environment variable is actually set - a recovery path for a value that failed to cache the first time. But pseudo_init_util()'s own tail calls pseudo_get_value("PSEUDO_DEBUG") and pseudo_get_value("PSEUDO_EVLOG") while it is still running, and it clears its own initted guard (pseudo_util_initted = 0) before those calls rather than after. If storing a value failed (pseudo_set_value()'s strdup() returned NULL, which happens when init runs re-entrantly on the same thread, inside another allocator's own not-yet-finished process init), the recovery branch re-enters pseudo_init_util() unconditionally, which fails the same strdup() again, calls pseudo_get_value() again, and recurses until the stack overflows. Observed with mold 2.42.1: its bundled mimalloc statically overrides malloc/strdup process-wide, and under pseudo's LD_PRELOAD interception, mimalloc's own lazy process init can call a libc function pseudo wraps before mimalloc has anywhere safe to return an allocation from. See the companion patch to ports/linux/pseudo_wrappers.c for the syscall()/ prctl() half of that same interaction. Move the initted-guard reset to the end of pseudo_init_util(), after its own lookups, and gate the recovery re-init in pseudo_get_value() on that guard. A value that still could not be cached now surfaces through the existing "failed to save new value" diagnostic and this function returns NULL, instead of recursing. Verified against pseudo's own test suite (no change to the 18/45 tests that pass in this sandbox, which lacks chroot/openat2/renameat2 support - confirmed identical to pristine master) and against a real `gcc -fuse-ld=mold` build run under pseudo with PSEUDO_DEBUG set, matching what bitbake exports for every fakeroot task. Signed-off-by: Javier Tia <javier@peridio.com> --- pseudo_util.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)