GSubprocessLauncher with FD assignment can clash with g_spawn_async internal pipe
GSubprocessLauncher allows to pass FDs to the child process to be mapped to specified FD numbers. E.g. one can pass an open FD to a file, and have g_subprocess_launcher_spawn() assign that FD to FD 3 in the child.
However, there is one problem. GSubprocess's child_setup function, which does this FD assignment, is unaware of the child error report pipe that g_spawn_async uses to pass an error from the child process back to the parent process. The read end of the pipe (created in gspawn.c:fork_exec_with_pipes) is closed in the child, but the write end is (of course) open, and if by chance one of the FDs of GSubprocessLauncher has that FD number as its assign-to target, the GSubprocess child_setup will happily dup2 it over the child error report pipe.
Now just after calling the GSubprocess child_setup function, gspawn.c:do_exec() calls g_execute() which will call execv[e]. If that fails (e.g. because the passed argv[0] doesn't refer to an executable), then there are two unwanted effects:
-
gspawn.c:do_exec will write 2 integers to what it thinks is the the child error report pipe FD (write end), but that FD is now (a dup of) one of the ones passed to GSubprocessLauncher. E.g. if that was a FD to a file, that file's contents are now lost and the file instead contains 8 bytes of binary nonsense.
-
On the other end of the child error report pipe, in the parent, when gspawn.c:fork_exec_with_fds() calls read_ints(), it gets back 0 bytes read (and EOF), which for it means that spawning succeeded. Consequently, g_subprocess_launcher_spawn() returns a new GSubprocess, that refers to a nonexistent process, instead of NULL and a GError.
Attached is a testcase that demonstrates the problem; run like this:
$ gcc $(pkg-config --cflags --libs glib-2.0 gio-2.0) -ldl -o test test.c
$ echo -n "ABCDEFGH">./input; hexdump -C ./input
00000000 41 42 43 44 45 46 47 48 |ABCDEFGH|
$ ./test ./input
[....]
Spawning successful, PID 272061
$ hexdump -C ./input
00000000 01 00 00 00 02 00 00 00 |........|
The same problem is probably also exhibited by other users of g_spawn_async with child setup functions that re-assign FDs, e.g. flatpak's org.freedesktop.portal.Flatpak.Spawn (code in flatpak.git/src/flatpak-portal:child_setup_func()).