This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Avoid deadlock with PERL_MEM_LOG
authorKarl Williamson <khw@cpan.org>
Thu, 26 Nov 2020 01:20:28 +0000 (18:20 -0700)
committerKarl Williamson <khw@cpan.org>
Fri, 27 Nov 2020 04:03:07 +0000 (21:03 -0700)
This fixes GH #18341

The Perl wrapper for getenv() was changed in 5.32 to allocate memory to
squirrel safely away the result of the wrapped getenv() call.  It does
this while in a critical section so as to make sure another thread can't
interrupt it and destroy it.

Unfortunately, when Perl is compiled for debugging memory problems and
has PERL_MEM_LOG enabled, that allocation causes a recursive call to
getenv() for the purpose of checking an environment variable to see how
to log that allocation.  And hence it deadlocks trying to enter the
critical section.

There are various solutions.  One is to use or emulate a general semaphore
instead of a binary one.  This is effectively what
PL_lc_numeric_mutex_depth does for another mutex, and the code for that
could be used as a template.

But given that this is an extreme edge case which requires Perl to be
specially compiled to enable this feature which is used only for
debugging, a much simpler, if less safe if it were to ever be used in
production, solution should suffice.  Tony Cook suggested just avoiding
the wrapper for this particular purpose.

util.c

diff --git a/util.c b/util.c
index 5989a58..40e6b8d 100644 (file)
--- a/util.c
+++ b/util.c
@@ -5008,7 +5008,11 @@ S_mem_log_common(enum mem_log_type mlt, const UV n,
 
     PERL_ARGS_ASSERT_MEM_LOG_COMMON;
 
-    pmlenv = PerlEnv_getenv("PERL_MEM_LOG");
+    /* Use plain getenv() to avoid potential deadlock with PerlEnv_getenv().
+     * This means that 'pmlenv' is not protected from other threads overwriting
+     * it on platforms where getenv() returns an internal static pointer.  See
+     * GH #18341 */
+    pmlenv = getenv("PERL_MEM_LOG");
     if (!pmlenv)
        return;
     if (mlt < MLT_NEW_SV ? strchr(pmlenv,'m') : strchr(pmlenv,'s'))