150 lines
3.7 KiB
Diff
150 lines
3.7 KiB
Diff
|
|
From f2917459f745bebf931bccd5cc2c33aa81ef4d12 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Peter Wu <peter@lekensteyn.nl>
|
||
|
|
Date: Sat, 24 Nov 2018 13:22:57 +0100
|
||
|
|
Subject: [PATCH 301/682] gspawn: Fix g_spawn deadlock in a multi-threaded
|
||
|
|
program on Linux
|
||
|
|
|
||
|
|
opendir and closedir are not async-signal-safe, these may call malloc
|
||
|
|
under the hood and cause a deadlock in a multi-threaded program.
|
||
|
|
This only affected Linux when /proc is mounted, other systems use a
|
||
|
|
slower path that iterates through all potential file descriptors.
|
||
|
|
Fixes a long-standing problem (since GLib 2.14.2).
|
||
|
|
|
||
|
|
Closes #945 and #1014
|
||
|
|
---
|
||
|
|
glib/gspawn.c | 94 +++++++++++++++++++++++++++++++++++----------------
|
||
|
|
1 file changed, 65 insertions(+), 29 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/glib/gspawn.c b/glib/gspawn.c
|
||
|
|
index 23ade06ae..69d3fec10 100644
|
||
|
|
--- a/glib/gspawn.c
|
||
|
|
+++ b/glib/gspawn.c
|
||
|
|
@@ -47,6 +47,10 @@
|
||
|
|
#include <sys/resource.h>
|
||
|
|
#endif /* HAVE_SYS_RESOURCE_H */
|
||
|
|
|
||
|
|
+#ifdef __linux__
|
||
|
|
+#include <sys/syscall.h> /* for syscall and SYS_getdents64 */
|
||
|
|
+#endif
|
||
|
|
+
|
||
|
|
#include "gspawn.h"
|
||
|
|
#include "gspawn-private.h"
|
||
|
|
#include "gthread.h"
|
||
|
|
@@ -1125,6 +1129,44 @@ set_cloexec (void *data, gint fd)
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef HAVE_FDWALK
|
||
|
|
+#ifdef __linux__
|
||
|
|
+struct linux_dirent64
|
||
|
|
+{
|
||
|
|
+ guint64 d_ino; /* 64-bit inode number */
|
||
|
|
+ guint64 d_off; /* 64-bit offset to next structure */
|
||
|
|
+ unsigned short d_reclen; /* Size of this dirent */
|
||
|
|
+ unsigned char d_type; /* File type */
|
||
|
|
+ char d_name[]; /* Filename (null-terminated) */
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+static gint
|
||
|
|
+filename_to_fd (const char *p)
|
||
|
|
+{
|
||
|
|
+ char c;
|
||
|
|
+ int fd = 0;
|
||
|
|
+ const int cutoff = G_MAXINT / 10;
|
||
|
|
+ const int cutlim = G_MAXINT % 10;
|
||
|
|
+
|
||
|
|
+ if (*p == '\0')
|
||
|
|
+ return -1;
|
||
|
|
+
|
||
|
|
+ while ((c = *p++) != '\0')
|
||
|
|
+ {
|
||
|
|
+ if (!g_ascii_isdigit (c))
|
||
|
|
+ return -1;
|
||
|
|
+ c -= '0';
|
||
|
|
+
|
||
|
|
+ /* Check for overflow. */
|
||
|
|
+ if (fd > cutoff || (fd == cutoff && c > cutlim))
|
||
|
|
+ return -1;
|
||
|
|
+
|
||
|
|
+ fd = fd * 10 + c;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ return fd;
|
||
|
|
+}
|
||
|
|
+#endif
|
||
|
|
+
|
||
|
|
static int
|
||
|
|
fdwalk (int (*cb)(void *data, int fd), void *data)
|
||
|
|
{
|
||
|
|
@@ -1136,45 +1178,39 @@ fdwalk (int (*cb)(void *data, int fd), void *data)
|
||
|
|
struct rlimit rl;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
-#ifdef __linux__
|
||
|
|
- DIR *d;
|
||
|
|
-
|
||
|
|
- if ((d = opendir("/proc/self/fd"))) {
|
||
|
|
- struct dirent *de;
|
||
|
|
-
|
||
|
|
- while ((de = readdir(d))) {
|
||
|
|
- glong l;
|
||
|
|
- gchar *e = NULL;
|
||
|
|
-
|
||
|
|
- if (de->d_name[0] == '.')
|
||
|
|
- continue;
|
||
|
|
-
|
||
|
|
- errno = 0;
|
||
|
|
- l = strtol(de->d_name, &e, 10);
|
||
|
|
- if (errno != 0 || !e || *e)
|
||
|
|
- continue;
|
||
|
|
-
|
||
|
|
- fd = (gint) l;
|
||
|
|
+#ifdef __linux__
|
||
|
|
+ /* Avoid use of opendir/closedir since these are not async-signal-safe. */
|
||
|
|
+ int dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
|
||
|
|
+ if (dir_fd >= 0)
|
||
|
|
+ {
|
||
|
|
+ char buf[4096];
|
||
|
|
+ int pos, nread;
|
||
|
|
+ struct linux_dirent64 *de;
|
||
|
|
|
||
|
|
- if ((glong) fd != l)
|
||
|
|
- continue;
|
||
|
|
+ while ((nread = syscall (SYS_getdents64, dir_fd, buf, sizeof(buf))) > 0)
|
||
|
|
+ {
|
||
|
|
+ for (pos = 0; pos < nread; pos += de->d_reclen)
|
||
|
|
+ {
|
||
|
|
+ de = (struct linux_dirent64 *)(buf + pos);
|
||
|
|
|
||
|
|
- if (fd == dirfd(d))
|
||
|
|
- continue;
|
||
|
|
+ fd = filename_to_fd (de->d_name);
|
||
|
|
+ if (fd < 0 || fd == dir_fd)
|
||
|
|
+ continue;
|
||
|
|
|
||
|
|
- if ((res = cb (data, fd)) != 0)
|
||
|
|
- break;
|
||
|
|
+ if ((res = cb (data, fd)) != 0)
|
||
|
|
+ break;
|
||
|
|
+ }
|
||
|
|
}
|
||
|
|
-
|
||
|
|
- closedir(d);
|
||
|
|
+
|
||
|
|
+ close (dir_fd);
|
||
|
|
return res;
|
||
|
|
- }
|
||
|
|
+ }
|
||
|
|
|
||
|
|
/* If /proc is not mounted or not accessible we fall back to the old
|
||
|
|
* rlimit trick */
|
||
|
|
|
||
|
|
#endif
|
||
|
|
-
|
||
|
|
+
|
||
|
|
#ifdef HAVE_SYS_RESOURCE_H
|
||
|
|
|
||
|
|
if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
|
||
|
|
--
|
||
|
|
2.19.1
|
||
|
|
|