summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2021-01-29 12:26:00 +0100
committerSimon McVittie <smcv@collabora.com>2021-01-31 13:37:13 +0000
commite864c6577ad45ecf614c63850ef059edf3313670 (patch)
tree338681996735acda2fb6958a66d41f5c245af452
parent82adfd7e3aa6a14fee78bc89fd9a2e3bb4617538 (diff)
downloadglib-e864c6577ad45ecf614c63850ef059edf3313670.tar.gz
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]
-rw-r--r--glib/gspawn.c38
1 files changed, 30 insertions, 8 deletions
diff --git a/glib/gspawn.c b/glib/gspawn.c
index 032835b93..3ed437423 100644
--- a/glib/gspawn.c
+++ b/glib/gspawn.c
@@ -1769,8 +1769,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
@@ -1864,7 +1866,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)
@@ -1876,12 +1888,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;
}
@@ -2128,8 +2150,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;
@@ -2163,8 +2185,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;
}