mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-28 23:16:14 +01:00
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
This commit is contained in:
parent
71bbe9f3e0
commit
f2917459f7
@ -47,6 +47,10 @@
|
|||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#endif /* HAVE_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.h"
|
||||||
#include "gspawn-private.h"
|
#include "gspawn-private.h"
|
||||||
#include "gthread.h"
|
#include "gthread.h"
|
||||||
@ -1125,6 +1129,44 @@ set_cloexec (void *data, gint fd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAVE_FDWALK
|
#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
|
static int
|
||||||
fdwalk (int (*cb)(void *data, int fd), void *data)
|
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;
|
struct rlimit rl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
DIR *d;
|
/* 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 ((d = opendir("/proc/self/fd"))) {
|
while ((nread = syscall (SYS_getdents64, dir_fd, buf, sizeof(buf))) > 0)
|
||||||
struct dirent *de;
|
{
|
||||||
|
for (pos = 0; pos < nread; pos += de->d_reclen)
|
||||||
|
{
|
||||||
|
de = (struct linux_dirent64 *)(buf + pos);
|
||||||
|
|
||||||
while ((de = readdir(d))) {
|
fd = filename_to_fd (de->d_name);
|
||||||
glong l;
|
if (fd < 0 || fd == dir_fd)
|
||||||
gchar *e = NULL;
|
continue;
|
||||||
|
|
||||||
if (de->d_name[0] == '.')
|
if ((res = cb (data, fd)) != 0)
|
||||||
continue;
|
break;
|
||||||
|
}
|
||||||
errno = 0;
|
|
||||||
l = strtol(de->d_name, &e, 10);
|
|
||||||
if (errno != 0 || !e || *e)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
fd = (gint) l;
|
|
||||||
|
|
||||||
if ((glong) fd != l)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (fd == dirfd(d))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ((res = cb (data, fd)) != 0)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closedir(d);
|
close (dir_fd);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If /proc is not mounted or not accessible we fall back to the old
|
/* If /proc is not mounted or not accessible we fall back to the old
|
||||||
* rlimit trick */
|
* rlimit trick */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SYS_RESOURCE_H
|
#ifdef HAVE_SYS_RESOURCE_H
|
||||||
|
|
||||||
if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
|
if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
|
||||||
|
Loading…
Reference in New Issue
Block a user