From 56644dfada06ae515057ff793d1a7402edf59931 Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Tue, 21 Apr 2009 17:21:06 -0400 Subject: [PATCH] Close file descriptors on re-exec Use code copied from GLib to close all file descriptors before we reexec ourselves on the restart Alt-F2 command. This fixes serious memory leaks when we have mapped graphics buffers. http://bugzilla.gnome.org/show_bug.cgi?id=579776 --- configure.ac | 3 ++ src/shell-global.c | 103 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index e3da44de3..0c52b6861 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,9 @@ GJS_JS_NATIVE_DIR=`$PKG_CONFIG --variable=jsnativedir gjs-1.0` AC_SUBST(GJS_JS_DIR) AC_SUBST(GJS_JS_NATIVE_DIR) +AC_CHECK_FUNCS(fdwalk) +AC_CHECK_HEADERS([sys/resource.h]) + # Sets GLIB_GENMARSHAL and GLIB_MKENUMS AM_PATH_GLIB_2_0() G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` diff --git a/src/shell-global.c b/src/shell-global.c index 2a2b7477f..c01dd51c7 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -7,10 +7,12 @@ #include #include #include -#include +#include #include +#include #include #include +#include #include #include @@ -592,6 +594,95 @@ shell_global_ungrab_keyboard (ShellGlobal *global) global->keyboard_grabbed = FALSE; } +/* Code to close all file descriptors before we exec; copied from gspawn.c in GLib. + * + * Authors: Padraig O'Briain, Matthias Clasen, Lennart Poettering + * + * http://bugzilla.gnome.org/show_bug.cgi?id=469231 + * http://bugzilla.gnome.org/show_bug.cgi?id=357585 + */ + +static int +set_cloexec (void *data, gint fd) +{ + if (fd >= GPOINTER_TO_INT (data)) + fcntl (fd, F_SETFD, FD_CLOEXEC); + + return 0; +} + +#ifndef HAVE_FDWALK +static int +fdwalk (int (*cb)(void *data, int fd), void *data) +{ + gint open_max; + gint fd; + gint res = 0; + +#ifdef HAVE_SYS_RESOURCE_H + 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; + + if ((glong) fd != l) + continue; + + if (fd == dirfd(d)) + continue; + + if ((res = cb (data, fd)) != 0) + break; + } + + closedir(d); + 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) + open_max = rl.rlim_max; + else +#endif + open_max = sysconf (_SC_OPEN_MAX); + + for (fd = 0; fd < open_max; fd++) + if ((res = cb (data, fd)) != 0) + break; + + return res; +} +#endif + +static void +pre_exec_close_fds(void) +{ + fdwalk (set_cloexec, GINT_TO_POINTER(3)); +} + /** * shell_global_reexec_self: * @global: A #ShellGlobal @@ -621,7 +712,15 @@ shell_global_reexec_self (ShellGlobal *global) for (buf_p = buf; buf_p < buf_end; buf_p = buf_p + strlen (buf_p) + 1) g_ptr_array_add (arr, buf_p); - g_ptr_array_add (arr, NULL); + g_ptr_array_add (arr, NULL); + + /* Close all file descriptors other than stdin/stdout/stderr, otherwise + * they will leak and stay open after the exec. In particular, this is + * important for file descriptors that represent mapped graphics buffer + * objects. + */ + pre_exec_close_fds (); + execvp (arr->pdata[0], (char**)arr->pdata); g_warning ("failed to reexec: %s", g_strerror (errno)); g_ptr_array_free (arr, TRUE);