gspawn: avoid race due to retry with EINTR on close()

Retry on EINTR is wrong on many OS, including Linux. See the comment
in g_close() why that is.

As we cannot use g_close() after fork, we had safe_close(). This had the
wrong retry loop on EINTR. Drop that.

This was especially problematic since commit 6f46294227f8 ('gspawn: Don’t
use g_close() in async-signal-safe context'). Before, safe_close() was
only called after fork, where there is only one thread and there is no
concern about a race.

This patch only exists for easier backporting of the bugfix. The code
will be reworked further next.

Fixes: 6f46294227f8 ('gspawn: Don’t use g_close() in async-signal-safe context')
This commit is contained in:
Thomas Haller 2022-10-12 23:30:37 +02:00 committed by Philip Withnall
parent 1936516268
commit f0bcb7f79c

View File

@ -162,7 +162,7 @@ extern char **environ;
*/ */
static gint safe_close (gint fd); static void safe_close (gint fd);
static gint g_execute (const gchar *file, static gint g_execute (const gchar *file,
gchar **argv, gchar **argv,
@ -1340,16 +1340,21 @@ dupfd_cloexec (int old_fd, int new_fd_min)
/* This function is called between fork() and exec() and hence must be /* This function is called between fork() and exec() and hence must be
* async-signal-safe (see signal-safety(7)). */ * async-signal-safe (see signal-safety(7)). */
static gint static void
safe_close (gint fd) safe_close (gint fd)
{ {
gint ret; /* Note that this function is (also) called after fork(), so it cannot use
* g_close().
do * Note that it is however called both from the parent and the child
ret = close (fd); * process.
while (ret < 0 && errno == EINTR); *
* This function returns no error, because there is nothing what the caller
return ret; * could do with that information. That is even the case for EINTR. See
* g_close() about the specialty of EINTR and why that is correct.
* If g_close() ever gets extended to handle EINTR specially, then this place
* and all other direct calls to close() need updating.
*/
close (fd);
} }
/* This function is called between fork() and exec() and hence must be /* This function is called between fork() and exec() and hence must be
@ -1358,7 +1363,7 @@ G_GNUC_UNUSED static int
close_func (void *data, int fd) close_func (void *data, int fd)
{ {
if (fd >= GPOINTER_TO_INT (data)) if (fd >= GPOINTER_TO_INT (data))
(void) safe_close (fd); safe_close (fd);
return 0; return 0;
} }