[vte] spawn: Fix g_spawn deadlock in a multi-threaded program on linux
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vte] spawn: Fix g_spawn deadlock in a multi-threaded program on linux
- Date: Mon, 26 Nov 2018 11:29:10 +0000 (UTC)
commit 8e35ff4ae7b4b10ab9b1c01ea35e0743d7542f5f
Author: Peter Wu <peter lekensteyn nl>
Date: Mon Nov 26 12:28:50 2018 +0100
spawn: Fix g_spawn deadlock in a multi-threaded program on linux
Ported from glib@f2917459f745bebf931bccd5cc2c33aa81ef4d12
Issues: glib#945 and glib#1014
src/vtespawn.cc | 94 +++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 65 insertions(+), 29 deletions(-)
---
diff --git a/src/vtespawn.cc b/src/vtespawn.cc
index 070ab780..71ee975b 100644
--- a/src/vtespawn.cc
+++ b/src/vtespawn.cc
@@ -38,6 +38,10 @@
#include <glib-unix.h>
+#ifdef __linux__
+#include <sys/syscall.h> /* for syscall and SYS_getdents64 */
+#endif
+
#include "vtespawn.hh"
#include "vteutils.h" /* for strchrnul on non-GNU systems */
#include "reaper.hh"
@@ -410,6 +414,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)
{
@@ -421,45 +463,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)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]