You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by gi...@apache.org on 2018/10/10 23:54:15 UTC

[mesos] 05/19: Avoided leaking file descriptors in Mesos containerizer.

This is an automated email from the ASF dual-hosted git repository.

gilbert pushed a commit to branch 1.4.x
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit 2e9293a727e78bcff7a0e58fd297aa5f329e8e00
Author: Benjamin Bannier <be...@mesosphere.io>
AuthorDate: Mon Jun 25 20:08:48 2018 +0200

    Avoided leaking file descriptors in Mesos containerizer.
    
    This patch explicitly closes not required file descriptors when
    forking a Mesos containerizer instance. We currently only pass on
    stdin, stout, and sterr.
    
    While it would in principle be possible to open all file descriptors
    with `O_CLOEXEC`, this is not practical as
    
    * `O_CLOEXEC` is not supported on BSDs (not Windows either, but file
      descriptor leaks are prevented there in a different way), and
    * we might call thirdparty code outside of our control which does not
      use e.g., `O_CLOEXEC` either (the case e.g., for leveldb).
    
    Closing all file descriptors after the fork avoids leaks even in above
    scenarios.
    
    Review: https://reviews.apache.org/r/67137/
    (cherry picked from commit 3f60c9857f8f68c3d2330ff3d03c8aa84b630db7)
---
 src/slave/containerizer/mesos/launch.cpp | 59 +++++++++++++++++++++++++++++++-
 1 file changed, 58 insertions(+), 1 deletion(-)

diff --git a/src/slave/containerizer/mesos/launch.cpp b/src/slave/containerizer/mesos/launch.cpp
index 0affcf5..2749b5d 100644
--- a/src/slave/containerizer/mesos/launch.cpp
+++ b/src/slave/containerizer/mesos/launch.cpp
@@ -29,8 +29,10 @@
 
 #include <stout/foreach.hpp>
 #include <stout/os.hpp>
-#include <stout/protobuf.hpp>
 #include <stout/path.hpp>
+#include <stout/protobuf.hpp>
+#include <stout/stringify.hpp>
+#include <stout/try.hpp>
 #include <stout/unreachable.hpp>
 
 #include <mesos/mesos.hpp>
@@ -57,6 +59,7 @@
 using std::cerr;
 using std::cout;
 using std::endl;
+using std::list;
 using std::set;
 using std::string;
 using std::vector;
@@ -230,6 +233,41 @@ static void exitWithStatus(int status)
 }
 
 
+// On Windows all new processes create by Mesos go through the
+// `create_process` wrapper which with the completion of MESOS-8926
+// will prevent inadvertent leaks making this code unnecessary there.
+//
+// TODO(bbannier): Consider moving this to stout as e.g., `os::lsof`.
+#ifndef __WINDOWS__
+static Try<hashset<int_fd>> getOpenFileDescriptors()
+{
+  Try<list<string>> fds =
+#if defined(__linux__)
+    os::ls("/proc/self/fd");
+#elif defined(__APPLE__)
+    os::ls("/dev/fd");
+#endif
+
+  if (fds.isError()) {
+    return Error(fds.error());
+  }
+
+  hashset<int_fd> result;
+  foreach (const string& fd, fds.get()) {
+    Try<int_fd> fd_ = numify<int_fd>(fd);
+
+    if (fd_.isError()) {
+      return Error("Could not interpret file descriptor: " + fd_.error());
+    }
+
+    result.insert(fd_.get());
+  }
+
+  return result;
+}
+#endif // __WINDOWS__
+
+
 int MesosContainerizerLaunch::execute()
 {
   if (flags.help) {
@@ -746,6 +784,18 @@ int MesosContainerizerLaunch::execute()
   }
 
 #ifndef __WINDOWS__
+  // Construct a set of file descriptors to close before `exec`'ing.
+  Try<hashset<int_fd>> fds = getOpenFileDescriptors();
+  CHECK_SOME(fds);
+
+  std::set<int_fd> whitelistedFds = {
+    STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
+
+  // Exclude required file descriptors from closing.
+  foreach (int_fd fd, whitelistedFds) {
+    fds->erase(fd);
+  }
+
   // If we have `containerStatusFd` set, then we need to fork-exec the
   // command we are launching and checkpoint its status on exit. We
   // use fork-exec directly (as opposed to `process::subprocess()`) to
@@ -816,6 +866,13 @@ int MesosContainerizerLaunch::execute()
       os::close(containerStatusFd.get());
       ::_exit(EXIT_SUCCESS);
     }
+
+    // Avoid leaking not required file descriptors into the forked process.
+    foreach (int_fd fd, fds.get()) {
+      // We use the unwrapped `::close` as opposed to `os::close`
+      // since the former is guaranteed to be async signal safe.
+      ::close(fd);
+    }
   }
 #endif // __WINDOWS__