[glib/glib-2-66: 3/5] spawn: prefer allocating buffers on stack for small sizes to avoid valgrind leaks




commit 8a3c3b8cd9496b74e0d284d4f4cad01f1bbe07d3
Author: Thomas Haller <thaller redhat com>
Date:   Fri Jan 29 12:26:00 2021 +0100

    spawn: prefer allocating buffers on stack for small sizes to avoid valgrind leaks
    
    We preallocate buffers that are used after forked. That is because
    malloc()/free() are not async-signal-safe and must not be used between
    fork() and exec().
    
    However, for the child process that exits without fork, valgrind wrongly
    reports these buffers as leaked.
    That can be suppressed with "--child-silent-after-fork=yes", but it is
    cumbersome.
    
    Work around by trying to allocate the buffers on the stack. At
    least in the common cases where the pointers are small enough
    so that we can reasonably do that.
    
    If the buffers happen to be large, we still allocate them on the heap
    and the problem still happens. Maybe we could have also allocated them
    as thread_local, but currently glib doesn't use that.
    
    [smcv: Cosmetic adjustments to address review comments from pwithnall]

 glib/gspawn.c | 38 ++++++++++++++++++++++++++++++--------
 1 file changed, 30 insertions(+), 8 deletions(-)
---
diff --git a/glib/gspawn.c b/glib/gspawn.c
index 0b9473022..c8e0ca806 100644
--- a/glib/gspawn.c
+++ b/glib/gspawn.c
@@ -1753,8 +1753,10 @@ fork_exec_with_fds (gboolean              intermediate_child,
   gint status;
   const gchar *chosen_search_path;
   gchar *search_path_buffer = NULL;
+  gchar *search_path_buffer_heap = NULL;
   gsize search_path_buffer_len = 0;
   gchar **argv_buffer = NULL;
+  gchar **argv_buffer_heap = NULL;
   gsize argv_buffer_len = 0;
 
 #ifdef POSIX_SPAWN_AVAILABLE
@@ -1848,7 +1850,17 @@ fork_exec_with_fds (gboolean              intermediate_child,
   if (chosen_search_path != NULL)
     {
       search_path_buffer_len = strlen (chosen_search_path) + strlen (argv[0]) + 2;
-      search_path_buffer = g_malloc (search_path_buffer_len);
+      if (search_path_buffer_len < 4000)
+        {
+          /* Prefer small stack allocations to avoid valgrind leak warnings
+           * in forked child. The 4000B cutoff is arbitrary. */
+          search_path_buffer = g_alloca (search_path_buffer_len);
+        }
+      else
+        {
+          search_path_buffer_heap = g_malloc (search_path_buffer_len);
+          search_path_buffer = search_path_buffer_heap;
+        }
     }
 
   if (search_path || search_path_from_envp)
@@ -1860,12 +1872,22 @@ fork_exec_with_fds (gboolean              intermediate_child,
    * script_execute() has to be called later on, it can build a wrapper argv
    * array in this buffer. */
   argv_buffer_len = g_strv_length (argv) + 2;
-  argv_buffer = g_new (gchar *, argv_buffer_len);
+  if (argv_buffer_len < 4000 / sizeof (gchar *))
+    {
+      /* Prefer small stack allocations to avoid valgrind leak warnings
+       * in forked child. The 4000B cutoff is arbitrary. */
+      argv_buffer = g_newa (gchar *, argv_buffer_len);
+    }
+  else
+    {
+      argv_buffer_heap = g_new (gchar *, argv_buffer_len);
+      argv_buffer = argv_buffer_heap;
+    }
 
   if (!g_unix_open_pipe (child_err_report_pipe, pipe_flags, error))
     {
-      g_free (search_path_buffer);
-      g_free (argv_buffer);
+      g_free (search_path_buffer_heap);
+      g_free (argv_buffer_heap);
       return FALSE;
     }
 
@@ -2112,8 +2134,8 @@ fork_exec_with_fds (gboolean              intermediate_child,
       close_and_invalidate (&child_err_report_pipe[0]);
       close_and_invalidate (&child_pid_report_pipe[0]);
 
-      g_free (search_path_buffer);
-      g_free (argv_buffer);
+      g_free (search_path_buffer_heap);
+      g_free (argv_buffer_heap);
 
       if (child_pid)
         *child_pid = pid;
@@ -2147,8 +2169,8 @@ fork_exec_with_fds (gboolean              intermediate_child,
   close_and_invalidate (&child_pid_report_pipe[0]);
   close_and_invalidate (&child_pid_report_pipe[1]);
 
-  g_free (search_path_buffer);
-  g_free (argv_buffer);
+  g_free (search_path_buffer_heap);
+  g_free (argv_buffer_heap);
 
   return FALSE;
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]