Merge branch 'spawn-check-privfds' into 'main'

glib/spawn: check user source_fds doesn't contain private fds

See merge request GNOME/glib!2479
This commit is contained in:
Philip Withnall 2022-12-12 13:53:59 +00:00
commit 7cd0750a41
4 changed files with 86 additions and 2 deletions

View File

@ -24,6 +24,7 @@
#include <errno.h>
#include "glibintl.h"
#include "gspawn.h"
static inline gint
@ -115,3 +116,24 @@ _g_spawn_exec_err_to_g_error (gint en)
return G_SPAWN_ERROR_FAILED;
}
}
static inline gboolean
_g_spawn_invalid_source_fd (gint fd,
const gint *source_fds,
gsize n_fds,
GError **error)
{
gsize i;
for (i = 0; i < n_fds; i++)
if (fd == source_fds[i])
{
g_set_error (error,
G_SPAWN_ERROR,
G_SPAWN_ERROR_INVAL,
_("Invalid source FDs argument"));
return TRUE;
}
return FALSE;
}

View File

@ -658,6 +658,9 @@ fork_exec (gint *exit_status,
{
if (!make_pipe (stdin_pipe, error))
goto cleanup_and_fail;
if (_g_spawn_invalid_source_fd (stdin_pipe[0], source_fds, n_fds, error) ||
_g_spawn_invalid_source_fd (stdin_pipe[1], source_fds, n_fds, error))
goto cleanup_and_fail;
stdin_fd = stdin_pipe[0];
}
@ -665,6 +668,9 @@ fork_exec (gint *exit_status,
{
if (!make_pipe (stdout_pipe, error))
goto cleanup_and_fail;
if (_g_spawn_invalid_source_fd (stdout_pipe[0], source_fds, n_fds, error) ||
_g_spawn_invalid_source_fd (stdout_pipe[1], source_fds, n_fds, error))
goto cleanup_and_fail;
stdout_fd = stdout_pipe[1];
}
@ -672,6 +678,9 @@ fork_exec (gint *exit_status,
{
if (!make_pipe (stderr_pipe, error))
goto cleanup_and_fail;
if (_g_spawn_invalid_source_fd (stderr_pipe[0], source_fds, n_fds, error) ||
_g_spawn_invalid_source_fd (stderr_pipe[1], source_fds, n_fds, error))
goto cleanup_and_fail;
stderr_fd = stderr_pipe[1];
}
@ -703,9 +712,15 @@ fork_exec (gint *exit_status,
if (!make_pipe (child_err_report_pipe, error))
goto cleanup_and_fail;
if (_g_spawn_invalid_source_fd (child_err_report_pipe[0], source_fds, n_fds, error) ||
_g_spawn_invalid_source_fd (child_err_report_pipe[1], source_fds, n_fds, error))
goto cleanup_and_fail;
if (!make_pipe (helper_sync_pipe, error))
goto cleanup_and_fail;
if (_g_spawn_invalid_source_fd (helper_sync_pipe[0], source_fds, n_fds, error) ||
_g_spawn_invalid_source_fd (helper_sync_pipe[1], source_fds, n_fds, error))
goto cleanup_and_fail;
new_argv = g_new (char *, argc + 1 + ARG_COUNT);
if (might_be_console_process ())

View File

@ -2334,6 +2334,9 @@ fork_exec (gboolean intermediate_child,
{
if (!g_unix_open_pipe (stdin_pipe, pipe_flags, error))
goto cleanup_and_fail;
if (_g_spawn_invalid_source_fd (stdin_pipe[0], source_fds, n_fds, error) ||
_g_spawn_invalid_source_fd (stdin_pipe[1], source_fds, n_fds, error))
goto cleanup_and_fail;
child_close_fds[n_child_close_fds++] = stdin_pipe[1];
stdin_fd = stdin_pipe[0];
}
@ -2342,6 +2345,9 @@ fork_exec (gboolean intermediate_child,
{
if (!g_unix_open_pipe (stdout_pipe, pipe_flags, error))
goto cleanup_and_fail;
if (_g_spawn_invalid_source_fd (stdout_pipe[0], source_fds, n_fds, error) ||
_g_spawn_invalid_source_fd (stdout_pipe[1], source_fds, n_fds, error))
goto cleanup_and_fail;
child_close_fds[n_child_close_fds++] = stdout_pipe[0];
stdout_fd = stdout_pipe[1];
}
@ -2350,6 +2356,9 @@ fork_exec (gboolean intermediate_child,
{
if (!g_unix_open_pipe (stderr_pipe, pipe_flags, error))
goto cleanup_and_fail;
if (_g_spawn_invalid_source_fd (stderr_pipe[0], source_fds, n_fds, error) ||
_g_spawn_invalid_source_fd (stderr_pipe[1], source_fds, n_fds, error))
goto cleanup_and_fail;
child_close_fds[n_child_close_fds++] = stderr_pipe[0];
stderr_fd = stderr_pipe[1];
}
@ -2491,9 +2500,18 @@ fork_exec (gboolean intermediate_child,
if (!g_unix_open_pipe (child_err_report_pipe, pipe_flags, error))
goto cleanup_and_fail;
if (intermediate_child && !g_unix_open_pipe (child_pid_report_pipe, pipe_flags, error))
if (_g_spawn_invalid_source_fd (child_err_report_pipe[0], source_fds, n_fds, error) ||
_g_spawn_invalid_source_fd (child_err_report_pipe[1], source_fds, n_fds, error))
goto cleanup_and_fail;
if (intermediate_child)
{
if (!g_unix_open_pipe (child_pid_report_pipe, pipe_flags, error))
goto cleanup_and_fail;
if (_g_spawn_invalid_source_fd (child_pid_report_pipe[0], source_fds, n_fds, error) ||
_g_spawn_invalid_source_fd (child_pid_report_pipe[1], source_fds, n_fds, error))
goto cleanup_and_fail;
}
pid = fork ();

View File

@ -318,6 +318,34 @@ test_spawn_async_with_fds (void)
g_free (arg);
}
static void
test_spawn_async_with_invalid_fds (void)
{
const gchar *argv[] = { echo_prog_path, "thread 0", NULL };
gint source_fds[1000];
GError *local_error = NULL;
gboolean retval;
gsize i;
/* this is very likely going to conflict with the internal fds, if not then skip */
for (i = 3; i < G_N_ELEMENTS (source_fds); i++)
source_fds[i] = i;
retval = g_spawn_async_with_pipes_and_fds (NULL, argv, NULL, G_SPAWN_DEFAULT,
NULL, NULL, -1, -1, -1,
source_fds, source_fds, G_N_ELEMENTS (source_fds),
NULL, NULL, NULL, NULL,
&local_error);
if (retval)
{
g_test_skip ("Skipping internal FDs check as test didnt manage to trigger a collision");
return;
}
g_assert_false (retval);
g_assert_error (local_error, G_SPAWN_ERROR, G_SPAWN_ERROR_INVAL);
g_error_free (local_error);
}
static void
test_spawn_sync (void)
{
@ -576,6 +604,7 @@ main (int argc,
g_test_add_func ("/gthread/spawn-stderr-socket", test_spawn_stderr_socket);
g_test_add_func ("/gthread/spawn-single-async", test_spawn_async);
g_test_add_func ("/gthread/spawn-single-async-with-fds", test_spawn_async_with_fds);
g_test_add_func ("/gthread/spawn-async-with-invalid-fds", test_spawn_async_with_invalid_fds);
g_test_add_func ("/gthread/spawn-script", test_spawn_script);
g_test_add_func ("/gthread/spawn/nonexistent", test_spawn_nonexistent);
g_test_add_func ("/gthread/spawn-posix-spawn", test_posix_spawn);