|
|
0d8777 |
From 68d6a69a8bc2f25e935608344d5b7e2b52cde85f Mon Sep 17 00:00:00 2001
|
|
|
0d8777 |
From: Ken Gaillot <kgaillot@redhat.com>
|
|
|
0d8777 |
Date: Mon, 21 Oct 2019 19:02:47 -0500
|
|
|
0d8777 |
Subject: [PATCH] Low: libcrmservice: don't close descriptors above current
|
|
|
0d8777 |
limit
|
|
|
0d8777 |
|
|
|
0d8777 |
This is irrelevant in normal use. However, valgrind can open high-numbered
|
|
|
0d8777 |
file descriptors for its own use above the soft limit of the process being run
|
|
|
0d8777 |
under valgrind. If that process forks a child that tries to close all open file
|
|
|
0d8777 |
descriptors (e.g. the executor running an agent), the close fails because the
|
|
|
0d8777 |
file descriptors are invalid, and (ironically) valgrind warns about that.
|
|
|
0d8777 |
|
|
|
0d8777 |
This allows 5a73027 to work under valgrind. Additionally, we extend the
|
|
|
0d8777 |
efficient close method from that commit to pacemakerd's spawning of children.
|
|
|
0d8777 |
---
|
|
|
0d8777 |
daemons/pacemakerd/pacemakerd.c | 10 ++----
|
|
|
0d8777 |
include/crm/common/internal.h | 3 ++
|
|
|
0d8777 |
lib/common/io.c | 72 +++++++++++++++++++++++++++++++++++++++++
|
|
|
0d8777 |
lib/services/services_linux.c | 29 +----------------
|
|
|
0d8777 |
4 files changed, 79 insertions(+), 35 deletions(-)
|
|
|
0d8777 |
|
|
|
0d8777 |
diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
|
|
|
0d8777 |
index fdc1d9f..d8ff53d 100644
|
|
|
0d8777 |
--- a/daemons/pacemakerd/pacemakerd.c
|
|
|
0d8777 |
+++ b/daemons/pacemakerd/pacemakerd.c
|
|
|
0d8777 |
@@ -13,6 +13,8 @@
|
|
|
0d8777 |
#include <pwd.h>
|
|
|
0d8777 |
#include <grp.h>
|
|
|
0d8777 |
#include <poll.h>
|
|
|
0d8777 |
+#include <stdio.h>
|
|
|
0d8777 |
+#include <stdbool.h>
|
|
|
0d8777 |
#include <sys/stat.h>
|
|
|
0d8777 |
#include <sys/types.h>
|
|
|
0d8777 |
#include <sys/time.h>
|
|
|
0d8777 |
@@ -290,10 +292,8 @@ static char *opts_vgrind[] = { NULL, NULL, NULL, NULL, NULL };
|
|
|
0d8777 |
static gboolean
|
|
|
0d8777 |
start_child(pcmk_child_t * child)
|
|
|
0d8777 |
{
|
|
|
0d8777 |
- int lpc = 0;
|
|
|
0d8777 |
uid_t uid = 0;
|
|
|
0d8777 |
gid_t gid = 0;
|
|
|
0d8777 |
- struct rlimit oflimits;
|
|
|
0d8777 |
gboolean use_valgrind = FALSE;
|
|
|
0d8777 |
gboolean use_callgrind = FALSE;
|
|
|
0d8777 |
const char *devnull = "/dev/null";
|
|
|
0d8777 |
@@ -396,11 +396,7 @@ start_child(pcmk_child_t * child)
|
|
|
0d8777 |
crm_perror(LOG_ERR, "Could not set user to %d (%s)", uid, child->uid);
|
|
|
0d8777 |
}
|
|
|
0d8777 |
|
|
|
0d8777 |
- /* Close all open file descriptors */
|
|
|
0d8777 |
- getrlimit(RLIMIT_NOFILE, &oflimits);
|
|
|
0d8777 |
- for (lpc = 0; lpc < oflimits.rlim_cur; lpc++) {
|
|
|
0d8777 |
- close(lpc);
|
|
|
0d8777 |
- }
|
|
|
0d8777 |
+ pcmk__close_fds_in_child(true);
|
|
|
0d8777 |
|
|
|
0d8777 |
(void)open(devnull, O_RDONLY); /* Stdin: fd 0 */
|
|
|
0d8777 |
(void)open(devnull, O_WRONLY); /* Stdout: fd 1 */
|
|
|
0d8777 |
diff --git a/include/crm/common/internal.h b/include/crm/common/internal.h
|
|
|
0d8777 |
index b2eec00..da2c7d7 100644
|
|
|
0d8777 |
--- a/include/crm/common/internal.h
|
|
|
0d8777 |
+++ b/include/crm/common/internal.h
|
|
|
0d8777 |
@@ -13,6 +13,7 @@
|
|
|
0d8777 |
#include <glib.h> /* for gboolean */
|
|
|
0d8777 |
#include <dirent.h> /* for struct dirent */
|
|
|
0d8777 |
#include <unistd.h> /* for getpid() */
|
|
|
0d8777 |
+#include <stdbool.h> /* for bool */
|
|
|
0d8777 |
#include <sys/types.h> /* for uid_t and gid_t */
|
|
|
0d8777 |
|
|
|
0d8777 |
#include <crm/common/logging.h>
|
|
|
0d8777 |
@@ -33,6 +34,8 @@ int crm_write_sync(int fd, const char *contents);
|
|
|
0d8777 |
int crm_set_nonblocking(int fd);
|
|
|
0d8777 |
const char *crm_get_tmpdir(void);
|
|
|
0d8777 |
|
|
|
0d8777 |
+void pcmk__close_fds_in_child(bool);
|
|
|
0d8777 |
+
|
|
|
0d8777 |
|
|
|
0d8777 |
/* internal procfs utilities (from procfs.c) */
|
|
|
0d8777 |
|
|
|
0d8777 |
diff --git a/lib/common/io.c b/lib/common/io.c
|
|
|
0d8777 |
index fa438dd..6cbab0a 100644
|
|
|
0d8777 |
--- a/lib/common/io.c
|
|
|
0d8777 |
+++ b/lib/common/io.c
|
|
|
0d8777 |
@@ -16,6 +16,7 @@
|
|
|
0d8777 |
#include <sys/param.h>
|
|
|
0d8777 |
#include <sys/types.h>
|
|
|
0d8777 |
#include <sys/stat.h>
|
|
|
0d8777 |
+#include <sys/resource.h>
|
|
|
0d8777 |
|
|
|
0d8777 |
#include <stdio.h>
|
|
|
0d8777 |
#include <unistd.h>
|
|
|
0d8777 |
@@ -501,3 +502,74 @@ crm_get_tmpdir()
|
|
|
0d8777 |
|
|
|
0d8777 |
return (dir && (*dir == '/'))? dir : "/tmp";
|
|
|
0d8777 |
}
|
|
|
0d8777 |
+
|
|
|
0d8777 |
+/*!
|
|
|
0d8777 |
+ * \internal
|
|
|
0d8777 |
+ * \brief Close open file descriptors
|
|
|
0d8777 |
+ *
|
|
|
0d8777 |
+ * Close all file descriptors (except optionally stdin, stdout, and stderr),
|
|
|
0d8777 |
+ * which is a best practice for a new child process forked for the purpose of
|
|
|
0d8777 |
+ * executing an external program.
|
|
|
0d8777 |
+ *
|
|
|
0d8777 |
+ * \param[in] bool If true, close stdin, stdout, and stderr as well
|
|
|
0d8777 |
+ */
|
|
|
0d8777 |
+void
|
|
|
0d8777 |
+pcmk__close_fds_in_child(bool all)
|
|
|
0d8777 |
+{
|
|
|
0d8777 |
+ DIR *dir;
|
|
|
0d8777 |
+ struct rlimit rlim;
|
|
|
0d8777 |
+ rlim_t max_fd;
|
|
|
0d8777 |
+ int min_fd = (all? 0 : (STDERR_FILENO + 1));
|
|
|
0d8777 |
+
|
|
|
0d8777 |
+ /* Find the current process's (soft) limit for open files. getrlimit()
|
|
|
0d8777 |
+ * should always work, but have a fallback just in case.
|
|
|
0d8777 |
+ */
|
|
|
0d8777 |
+ if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
|
|
|
0d8777 |
+ max_fd = rlim.rlim_cur - 1;
|
|
|
0d8777 |
+ } else {
|
|
|
0d8777 |
+ long conf_max = sysconf(_SC_OPEN_MAX);
|
|
|
0d8777 |
+
|
|
|
0d8777 |
+ max_fd = (conf_max > 0)? conf_max : 1024;
|
|
|
0d8777 |
+ }
|
|
|
0d8777 |
+
|
|
|
0d8777 |
+ /* /proc/self/fd (on Linux) or /dev/fd (on most OSes) contains symlinks to
|
|
|
0d8777 |
+ * all open files for the current process, named as the file descriptor.
|
|
|
0d8777 |
+ * Use this if available, because it's more efficient than a shotgun
|
|
|
0d8777 |
+ * approach to closing descriptors.
|
|
|
0d8777 |
+ */
|
|
|
0d8777 |
+#if SUPPORT_PROCFS
|
|
|
0d8777 |
+ dir = opendir("/proc/self/fd");
|
|
|
0d8777 |
+ if (dir == NULL) {
|
|
|
0d8777 |
+ dir = opendir("/dev/fd");
|
|
|
0d8777 |
+ }
|
|
|
0d8777 |
+#else
|
|
|
0d8777 |
+ dir = opendir("/dev/fd");
|
|
|
0d8777 |
+#endif
|
|
|
0d8777 |
+ if (dir != NULL) {
|
|
|
0d8777 |
+ struct dirent *entry;
|
|
|
0d8777 |
+ int dir_fd = dirfd(dir);
|
|
|
0d8777 |
+
|
|
|
0d8777 |
+ while ((entry = readdir(dir)) != NULL) {
|
|
|
0d8777 |
+ int lpc = atoi(entry->d_name);
|
|
|
0d8777 |
+
|
|
|
0d8777 |
+ /* How could one of these entries be higher than max_fd, you ask?
|
|
|
0d8777 |
+ * It isn't possible in normal operation, but when run under
|
|
|
0d8777 |
+ * valgrind, valgrind can open high-numbered file descriptors for
|
|
|
0d8777 |
+ * its own use that are higher than the process's soft limit.
|
|
|
0d8777 |
+ * These will show up in the fd directory but aren't closable.
|
|
|
0d8777 |
+ */
|
|
|
0d8777 |
+ if ((lpc >= min_fd) && (lpc <= max_fd) && (lpc != dir_fd)) {
|
|
|
0d8777 |
+ close(lpc);
|
|
|
0d8777 |
+ }
|
|
|
0d8777 |
+ }
|
|
|
0d8777 |
+ closedir(dir);
|
|
|
0d8777 |
+ return;
|
|
|
0d8777 |
+ }
|
|
|
0d8777 |
+
|
|
|
0d8777 |
+ /* If no fd directory is available, iterate over all possible descriptors.
|
|
|
0d8777 |
+ * This is less efficient due to the overhead of many system calls.
|
|
|
0d8777 |
+ */
|
|
|
0d8777 |
+ for (int lpc = max_fd; lpc >= min_fd; lpc--) {
|
|
|
0d8777 |
+ close(lpc);
|
|
|
0d8777 |
+ }
|
|
|
0d8777 |
+}
|
|
|
0d8777 |
diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c
|
|
|
0d8777 |
index 464fc5b..6870273 100644
|
|
|
0d8777 |
--- a/lib/services/services_linux.c
|
|
|
0d8777 |
+++ b/lib/services/services_linux.c
|
|
|
0d8777 |
@@ -444,9 +444,6 @@ services_handle_exec_error(svc_action_t * op, int error)
|
|
|
0d8777 |
static void
|
|
|
0d8777 |
action_launch_child(svc_action_t *op)
|
|
|
0d8777 |
{
|
|
|
0d8777 |
- int lpc;
|
|
|
0d8777 |
- DIR *dir;
|
|
|
0d8777 |
-
|
|
|
0d8777 |
/* SIGPIPE is ignored (which is different from signal blocking) by the gnutls library.
|
|
|
0d8777 |
* Depending on the libqb version in use, libqb may set SIGPIPE to be ignored as well.
|
|
|
0d8777 |
* We do not want this to be inherited by the child process. By resetting this the signal
|
|
|
0d8777 |
@@ -476,31 +473,7 @@ action_launch_child(svc_action_t *op)
|
|
|
0d8777 |
*/
|
|
|
0d8777 |
setpgid(0, 0);
|
|
|
0d8777 |
|
|
|
0d8777 |
- // Close all file descriptors except stdin/stdout/stderr
|
|
|
0d8777 |
-#if SUPPORT_PROCFS
|
|
|
0d8777 |
- dir = opendir("/proc/self/fd");
|
|
|
0d8777 |
-#else
|
|
|
0d8777 |
- dir = opendir("/dev/fd");
|
|
|
0d8777 |
-#endif
|
|
|
0d8777 |
- if (dir == NULL) { /* /proc or /dev/fd not available */
|
|
|
0d8777 |
- /* Iterate over all possible fds, might be slow */
|
|
|
0d8777 |
- for (lpc = getdtablesize() - 1; lpc > STDERR_FILENO; lpc--) {
|
|
|
0d8777 |
- close(lpc);
|
|
|
0d8777 |
- }
|
|
|
0d8777 |
- } else {
|
|
|
0d8777 |
- /* Iterate over fds obtained from /proc or /dev/fd */
|
|
|
0d8777 |
- struct dirent *entry;
|
|
|
0d8777 |
- int dir_fd = dirfd(dir);
|
|
|
0d8777 |
-
|
|
|
0d8777 |
- while ((entry = readdir(dir)) != NULL) {
|
|
|
0d8777 |
- lpc = atoi(entry->d_name);
|
|
|
0d8777 |
- if (lpc > STDERR_FILENO && lpc != dir_fd) {
|
|
|
0d8777 |
- close(lpc);
|
|
|
0d8777 |
- }
|
|
|
0d8777 |
- }
|
|
|
0d8777 |
-
|
|
|
0d8777 |
- closedir(dir);
|
|
|
0d8777 |
- }
|
|
|
0d8777 |
+ pcmk__close_fds_in_child(false);
|
|
|
0d8777 |
|
|
|
0d8777 |
#if SUPPORT_CIBSECRETS
|
|
|
0d8777 |
if (replace_secret_params(op->rsc, op->params) < 0) {
|
|
|
0d8777 |
--
|
|
|
0d8777 |
1.8.3.1
|
|
|
0d8777 |
|