You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by an...@apache.org on 2018/06/14 03:58:41 UTC

[1/3] mesos git commit: Whitelist inheritable file descriptors in stout.

Repository: mesos
Updated Branches:
  refs/heads/master 1b851877f -> f8c8c35af


Whitelist inheritable file descriptors in stout.

This commit adds additional information to the `CreateProcess` system
call on Windows that effectively whitelists the file descriptors that
the child is allowed to inherit. This helps avoid bugs due to race
conditions where spawned child processes inherited file descriptors
not meant for them.

Similar logic can be added to the POSIX APIs.

Review: https://reviews.apache.org/r/67286/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/c4ce8e17
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/c4ce8e17
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/c4ce8e17

Branch: refs/heads/master
Commit: c4ce8e170bdf6672e4632b58d2d8c22d3d75a60f
Parents: 1b85187
Author: Radhika Jandhyala <ra...@microsoft.com>
Authored: Wed Jun 13 15:53:32 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 13 16:36:09 2018 -0700

----------------------------------------------------------------------
 .../include/stout/internal/windows/inherit.hpp  | 67 ++++++++++++++++
 .../stout/include/stout/os/windows/shell.hpp    | 80 +++++++++++++++-----
 2 files changed, 129 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/c4ce8e17/3rdparty/stout/include/stout/internal/windows/inherit.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/include/stout/internal/windows/inherit.hpp b/3rdparty/stout/include/stout/internal/windows/inherit.hpp
index 7dbde82..fb50904 100644
--- a/3rdparty/stout/include/stout/internal/windows/inherit.hpp
+++ b/3rdparty/stout/include/stout/internal/windows/inherit.hpp
@@ -20,9 +20,76 @@
 
 #include <stout/os/int_fd.hpp>
 
+#include <processthreadsapi.h>
+
 namespace internal {
 namespace windows {
 
+// This function creates `LPPROC_THREAD_ATTRIBUTE_LIST`, which is used
+// to whitelist handles sent to a child process.
+typedef _PROC_THREAD_ATTRIBUTE_LIST AttributeList;
+inline Result<std::shared_ptr<AttributeList>>
+create_attributes_list_for_handles(const std::vector<HANDLE>& handles)
+{
+  if (handles.empty()) {
+    return None();
+  }
+
+  SIZE_T size = 0;
+  BOOL result = ::InitializeProcThreadAttributeList(
+      nullptr, // Pointer to _PROC_THREAD_ATTRIBUTE_LIST. This
+               // parameter can be `nullptr` to determine the buffer
+               // size required to support the specified number of
+               // attributes.
+      1,       // Count of attributes to be added to the list.
+      0,       // Reserved and must be zero.
+      &size);  // Size in bytes required for the attribute list.
+
+  if (result == FALSE) {
+    if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+      return WindowsError();
+    }
+  }
+
+  std::shared_ptr<AttributeList> attribute_list(
+      reinterpret_cast<AttributeList*>(std::malloc(size)),
+      [](AttributeList* p) {
+        // NOTE: This delete API does not return anything, nor can it
+        // fail, so it is safe to call it regardless of the
+        // initialization state of `p`.
+        ::DeleteProcThreadAttributeList(p);
+        std::free(p);
+      });
+
+  if (attribute_list == nullptr) {
+    // `std::malloc` failed.
+    return WindowsError(ERROR_OUTOFMEMORY);
+  }
+
+  result =
+    ::InitializeProcThreadAttributeList(attribute_list.get(), 1, 0, &size);
+
+  if (result == FALSE) {
+    return WindowsError();
+  }
+
+  result = ::UpdateProcThreadAttribute(
+      attribute_list.get(),               // Pointer to list structure.
+      0,                                  // Reserved and must be 0.
+      PROC_THREAD_ATTRIBUTE_HANDLE_LIST,  // The attribute key to update in the
+                                          // attribute list.
+      const_cast<PVOID*>(handles.data()), // Pointer to the attribute value.
+      handles.size() * sizeof(HANDLE),    // Size of the attribute value.
+      nullptr,                            // Reserved and must be `nullptr`.
+      nullptr);                           // Reserved and must be `nullptr`.
+
+  if (result == FALSE) {
+    return WindowsError();
+  }
+
+  return attribute_list;
+}
+
 // This function enables or disables inheritance for a Windows file handle.
 //
 // NOTE: By default, handles on Windows are not inheritable, so this is

http://git-wip-us.apache.org/repos/asf/mesos/blob/c4ce8e17/3rdparty/stout/include/stout/os/windows/shell.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/include/stout/os/windows/shell.hpp b/3rdparty/stout/include/stout/os/windows/shell.hpp
index 8afcaa6..b9e06d6 100644
--- a/3rdparty/stout/include/stout/os/windows/shell.hpp
+++ b/3rdparty/stout/include/stout/os/windows/shell.hpp
@@ -242,15 +242,18 @@ inline Try<ProcessData> create_process(
     const std::vector<std::string>& argv,
     const Option<std::map<std::string, std::string>>& environment,
     const bool create_suspended = false,
-    const Option<std::array<int_fd, 3>>& pipes = None())
+    const Option<std::array<int_fd, 3>>& pipes = None(),
+    const std::vector<int_fd>& whitelist_fds = {})
 {
   // TODO(andschwa): Assert that `command` and `argv[0]` are the same.
   const std::wstring arg_string = stringify_args(argv);
   std::vector<wchar_t> arg_buffer(arg_string.begin(), arg_string.end());
   arg_buffer.push_back(L'\0');
 
-  // Create the process with a Unicode environment.
-  DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT;
+  // Create the process with a Unicode environment and extended
+  // startup info.
+  DWORD creation_flags =
+    CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT;
   if (create_suspended) {
     creation_flags |= CREATE_SUSPENDED;
   }
@@ -267,28 +270,57 @@ inline Try<ProcessData> create_process(
 
   PROCESS_INFORMATION process_info = {};
 
-  STARTUPINFOW startup_info = {};
-  startup_info.cb = sizeof(STARTUPINFOW);
-
-  // Hook up the stdin/out/err pipes and use the `STARTF_USESTDHANDLES`
-  // flag to instruct the child to use them [1].
-  // A more user-friendly example can be found in [2].
-  //
-  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
-  // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
+  STARTUPINFOEXW startup_info_ex = {};
+  startup_info_ex.StartupInfo.cb = sizeof(startup_info_ex);
+
+  // Windows provides a way to whitelist a set of handles to be
+  // inherited by the child process.
+  // https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873
+  // (1) We're setting the pipe handles and whitelisted handles to be
+  //     temporarily inheritable.
+  // (2) We're explicitly whitelisting the handles using a Windows API.
+  // (3) We're then setting the handles to back to non-inheritable
+  //     after the child process has been created.
+  std::vector<HANDLE> handles;
   if (pipes.isSome()) {
     // Each of these handles must be inheritable.
     foreach (const int_fd& fd, pipes.get()) {
+      handles.emplace_back(static_cast<HANDLE>(fd));
       const Try<Nothing> inherit = set_inherit(fd, true);
       if (inherit.isError()) {
         return Error(inherit.error());
       }
     }
 
-    startup_info.dwFlags |= STARTF_USESTDHANDLES;
-    startup_info.hStdInput = std::get<0>(pipes.get());
-    startup_info.hStdOutput = std::get<1>(pipes.get());
-    startup_info.hStdError = std::get<2>(pipes.get());
+    // Hook up the stdin/out/err pipes and use the `STARTF_USESTDHANDLES`
+    // flag to instruct the child to use them [1].
+    // A more user-friendly example can be found in [2].
+    //
+    // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
+    // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
+    startup_info_ex.StartupInfo.dwFlags   |= STARTF_USESTDHANDLES;
+    startup_info_ex.StartupInfo.hStdInput  = std::get<0>(pipes.get());
+    startup_info_ex.StartupInfo.hStdOutput = std::get<1>(pipes.get());
+    startup_info_ex.StartupInfo.hStdError  = std::get<2>(pipes.get());
+  }
+
+  foreach (const int_fd& fd, whitelist_fds) {
+    handles.emplace_back(static_cast<HANDLE>(fd));
+    const Try<Nothing> inherit = set_inherit(fd, true);
+    if (inherit.isError()) {
+      return Error(inherit.error());
+    }
+  }
+
+  Result<std::shared_ptr<AttributeList>> attribute_list =
+    create_attributes_list_for_handles(handles);
+
+  if (attribute_list.isError()) {
+    return Error(attribute_list.error());
+  }
+
+  if (attribute_list.isSome()) {
+    startup_info_ex.lpAttributeList = attribute_list->get();
   }
 
   const BOOL result = ::CreateProcessW(
@@ -301,9 +333,14 @@ inline Try<ProcessData> create_process(
       creation_flags,
       static_cast<LPVOID>(process_env),
       static_cast<LPCWSTR>(nullptr), // Inherit working directory.
-      &startup_info,
+      &startup_info_ex.StartupInfo,
       &process_info);
 
+  // Save the error from the previous call so that we can proceed to
+  // always revert the inheritance of the handles, and then report
+  // this error, if there was one.
+  const DWORD create_process_error = ::GetLastError();
+
   // NOTE: The MSDN documentation for `CreateProcess` states that it
   // returns before the process has "finished initialization," but is
   // not clear on precisely what initialization entails. It would seem
@@ -312,7 +349,6 @@ inline Try<ProcessData> create_process(
   // handles to become inherited, and not some "initialization" of the
   // child process. However, if an inheritance race condition
   // manifests, this assumption should be re-evaluated.
-
   if (pipes.isSome()) {
     // These handles should no longer be inheritable. This prevents other child
     // processes from accidentally inheriting the wrong handles.
@@ -328,8 +364,16 @@ inline Try<ProcessData> create_process(
     }
   }
 
+  foreach (const int_fd& fd, whitelist_fds) {
+    const Try<Nothing> inherit = set_inherit(fd, false);
+    if (inherit.isError()) {
+      return Error(inherit.error());
+    }
+  }
+
   if (result == FALSE) {
     return WindowsError(
+        create_process_error,
         "Failed to call `CreateProcess`: " + stringify(arg_string));
   }
 


[2/3] mesos git commit: Whitelist inheritable file descriptors in libprocess.

Posted by an...@apache.org.
Whitelist inheritable file descriptors in libprocess.

This commit plumbs the list of whitelisted file descriptors through
the libprocess APIs.

Review: https://reviews.apache.org/r/67287/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/281cf5dd
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/281cf5dd
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/281cf5dd

Branch: refs/heads/master
Commit: 281cf5dd7239dce5103ee34b64df2b785672271f
Parents: c4ce8e1
Author: Radhika Jandhyala <ra...@microsoft.com>
Authored: Wed Jun 13 15:56:20 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 13 20:45:29 2018 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/include/process/subprocess.hpp | 15 ++++++++++-----
 3rdparty/libprocess/src/subprocess.cpp             |  6 ++++--
 3rdparty/libprocess/src/subprocess_windows.hpp     |  6 ++++--
 3 files changed, 18 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/281cf5dd/3rdparty/libprocess/include/process/subprocess.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/include/process/subprocess.hpp b/3rdparty/libprocess/include/process/subprocess.hpp
index 6a12623..135bf24 100644
--- a/3rdparty/libprocess/include/process/subprocess.hpp
+++ b/3rdparty/libprocess/include/process/subprocess.hpp
@@ -125,7 +125,8 @@ public:
         const Option<lambda::function<
             pid_t(const lambda::function<int()>&)>>& clone,
         const std::vector<Subprocess::ParentHook>& parent_hooks,
-        const std::vector<Subprocess::ChildHook>& child_hooks);
+        const std::vector<Subprocess::ChildHook>& child_hooks,
+        const std::vector<int_fd>& whitelist_fds);
 
     IO(const lambda::function<Try<InputFileDescriptors>()>& _input,
        const lambda::function<Try<OutputFileDescriptors>()>& _output)
@@ -305,7 +306,8 @@ private:
       const Option<lambda::function<
           pid_t(const lambda::function<int()>&)>>& clone,
       const std::vector<Subprocess::ParentHook>& parent_hooks,
-      const std::vector<Subprocess::ChildHook>& child_hooks);
+      const std::vector<Subprocess::ChildHook>& child_hooks,
+      const std::vector<int_fd>& whitelist_fds);
 
   struct Data
   {
@@ -377,7 +379,8 @@ Try<Subprocess> subprocess(
     const Option<lambda::function<
         pid_t(const lambda::function<int()>&)>>& clone = None(),
     const std::vector<Subprocess::ParentHook>& parent_hooks = {},
-    const std::vector<Subprocess::ChildHook>& child_hooks = {});
+    const std::vector<Subprocess::ChildHook>& child_hooks = {},
+    const std::vector<int_fd>& whitelist_fds = {});
 
 
 /**
@@ -413,7 +416,8 @@ inline Try<Subprocess> subprocess(
     const Option<lambda::function<
         pid_t(const lambda::function<int()>&)>>& clone = None(),
     const std::vector<Subprocess::ParentHook>& parent_hooks = {},
-    const std::vector<Subprocess::ChildHook>& child_hooks = {})
+    const std::vector<Subprocess::ChildHook>& child_hooks = {},
+    const std::vector<int_fd>& whitelist_fds = {})
 {
   std::vector<std::string> argv = {os::Shell::arg0, os::Shell::arg1, command};
 
@@ -427,7 +431,8 @@ inline Try<Subprocess> subprocess(
       environment,
       clone,
       parent_hooks,
-      child_hooks);
+      child_hooks,
+      whitelist_fds);
 }
 
 } // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/281cf5dd/3rdparty/libprocess/src/subprocess.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/subprocess.cpp b/3rdparty/libprocess/src/subprocess.cpp
index d7a7253..0b2c02a 100644
--- a/3rdparty/libprocess/src/subprocess.cpp
+++ b/3rdparty/libprocess/src/subprocess.cpp
@@ -331,7 +331,8 @@ Try<Subprocess> subprocess(
     const Option<lambda::function<
         pid_t(const lambda::function<int()>&)>>& _clone,
     const vector<Subprocess::ParentHook>& parent_hooks,
-    const vector<Subprocess::ChildHook>& child_hooks)
+    const vector<Subprocess::ChildHook>& child_hooks,
+    const vector<int_fd>& whitelist_fds)
 {
   // TODO(hausdorff): We should error out on Windows here if we are passing
   // parameters that aren't used.
@@ -430,7 +431,8 @@ Try<Subprocess> subprocess(
           parent_hooks,
           stdinfds,
           stdoutfds,
-          stderrfds);
+          stderrfds,
+          whitelist_fds);
 
     if (process_data.isError()) {
       // NOTE: `createChildProcess` either succeeds entirely or returns an

http://git-wip-us.apache.org/repos/asf/mesos/blob/281cf5dd/3rdparty/libprocess/src/subprocess_windows.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/subprocess_windows.hpp b/3rdparty/libprocess/src/subprocess_windows.hpp
index c7ed0ad..1bbb8af 100644
--- a/3rdparty/libprocess/src/subprocess_windows.hpp
+++ b/3rdparty/libprocess/src/subprocess_windows.hpp
@@ -51,7 +51,8 @@ inline Try<::internal::windows::ProcessData> createChildProcess(
     const std::vector<Subprocess::ParentHook>& parent_hooks,
     const InputFileDescriptors& stdinfds,
     const OutputFileDescriptors& stdoutfds,
-    const OutputFileDescriptors& stderrfds)
+    const OutputFileDescriptors& stderrfds,
+    const std::vector<int_fd>& whitelist_fds = {})
 {
   const std::array<int_fd, 3> fds{
     stdinfds.read, stdoutfds.write, stderrfds.write};
@@ -62,7 +63,8 @@ inline Try<::internal::windows::ProcessData> createChildProcess(
         argv,
         environment,
         true, // Create suspended.
-        fds);
+        fds,
+        whitelist_fds);
 
   // Close the child-ends of the file descriptors that are created
   // by this function.


[3/3] mesos git commit: Whitelist inheritable file descriptors in the containerizer.

Posted by an...@apache.org.
Whitelist inheritable file descriptors in the containerizer.

This commit whitelists the read and write ends of the control pipe
that are intended to be inherited by the containerizer. This is
necessary because this pipe is passed to the child process
implicitly (through environment variables), so previously the
libprocess and stout APIs had no knowledge that these file descriptors
needed to be inheritable.

Adding the whitelist as yet another parameter caused us to exceed the
mock methods of Google Mock, so we had to squash three other
parameters into one, name the `containerIO` in/out/err fields are
instead passed as one, unwrapped later.

Also, a new `forkImpl` method had to be added to the tests because it
is not possible to mock a function with default arguments in Google
Mock, but this can be worked around by mocking in an implementation.

Review: https://reviews.apache.org/r/67394/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/f8c8c35a
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/f8c8c35a
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/f8c8c35a

Branch: refs/heads/master
Commit: f8c8c35af920518ecb6c56d873dd44160390c5c7
Parents: 281cf5d
Author: Radhika Jandhyala <ra...@microsoft.com>
Authored: Wed Jun 13 15:57:31 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 13 20:45:29 2018 -0700

----------------------------------------------------------------------
 src/slave/containerizer/mesos/containerizer.cpp |  9 +++---
 src/slave/containerizer/mesos/launcher.cpp      | 16 ++++-----
 src/slave/containerizer/mesos/launcher.hpp      | 15 ++++-----
 .../containerizer/mesos/linux_launcher.cpp      | 34 +++++++++-----------
 .../containerizer/mesos/linux_launcher.hpp      |  7 ++--
 src/tests/containerizer/launcher.cpp            |  6 ++--
 src/tests/containerizer/launcher.hpp            |  9 +++---
 .../containerizer/mesos_containerizer_tests.cpp |  7 ++--
 8 files changed, 48 insertions(+), 55 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/f8c8c35a/src/slave/containerizer/mesos/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/containerizer.cpp b/src/slave/containerizer/mesos/containerizer.cpp
index 17a1a37..7b3b0d3 100644
--- a/src/slave/containerizer/mesos/containerizer.cpp
+++ b/src/slave/containerizer/mesos/containerizer.cpp
@@ -1893,6 +1893,8 @@ Future<Containerizer::LaunchResult> MesosContainerizerProcess::_launch(
   launchFlags.pipe_read = pipes[0];
   launchFlags.pipe_write = pipes[1];
 
+  const vector<int_fd> whitelistFds{pipes[0], pipes[1]};
+
 #ifndef __WINDOWS__
   // Set the `runtime_directory` launcher flag so that the launch
   // helper knows where to checkpoint the status of the container
@@ -1978,15 +1980,14 @@ Future<Containerizer::LaunchResult> MesosContainerizerProcess::_launch(
       containerId,
       argv[0],
       argv,
-      containerIO->in,
-      containerIO->out,
-      containerIO->err,
+      containerIO.get(),
       nullptr,
       launchEnvironment,
       // 'enterNamespaces' will be ignored by SubprocessLauncher.
       _enterNamespaces,
       // 'cloneNamespaces' will be ignored by SubprocessLauncher.
-      _cloneNamespaces);
+      _cloneNamespaces,
+      whitelistFds);
 
   if (forked.isError()) {
     return Failure("Failed to fork: " + forked.error());

http://git-wip-us.apache.org/repos/asf/mesos/blob/f8c8c35a/src/slave/containerizer/mesos/launcher.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/launcher.cpp b/src/slave/containerizer/mesos/launcher.cpp
index 9df05f3..ed41a14 100644
--- a/src/slave/containerizer/mesos/launcher.cpp
+++ b/src/slave/containerizer/mesos/launcher.cpp
@@ -84,13 +84,12 @@ Try<pid_t> SubprocessLauncher::fork(
     const ContainerID& containerId,
     const string& path,
     const vector<string>& argv,
-    const Subprocess::IO& in,
-    const Subprocess::IO& out,
-    const Subprocess::IO& err,
+    const mesos::slave::ContainerIO& containerIO,
     const flags::FlagsBase* flags,
     const Option<map<string, string>>& environment,
     const Option<int>& enterNamespaces,
-    const Option<int>& cloneNamespaces)
+    const Option<int>& cloneNamespaces,
+    const vector<int_fd>& whitelistFds)
 {
   if (enterNamespaces.isSome() && enterNamespaces.get() != 0) {
     return Error("Subprocess launcher does not support entering namespaces");
@@ -123,14 +122,15 @@ Try<pid_t> SubprocessLauncher::fork(
   Try<Subprocess> child = subprocess(
       path,
       argv,
-      in,
-      out,
-      err,
+      containerIO.in,
+      containerIO.out,
+      containerIO.err,
       flags,
       environment,
       None(),
       parentHooks,
-      {Subprocess::ChildHook::SETSID()});
+      {Subprocess::ChildHook::SETSID()},
+      whitelistFds);
 
   if (child.isError()) {
     return Error("Failed to fork a child process: " + child.error());

http://git-wip-us.apache.org/repos/asf/mesos/blob/f8c8c35a/src/slave/containerizer/mesos/launcher.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/launcher.hpp b/src/slave/containerizer/mesos/launcher.hpp
index 4b5ae73..c169b87 100644
--- a/src/slave/containerizer/mesos/launcher.hpp
+++ b/src/slave/containerizer/mesos/launcher.hpp
@@ -38,6 +38,7 @@
 #include <stout/try.hpp>
 
 #include "slave/flags.hpp"
+#include "slave/containerizer/containerizer.hpp"
 
 namespace mesos {
 namespace internal {
@@ -64,13 +65,12 @@ public:
       const ContainerID& containerId,
       const std::string& path,
       const std::vector<std::string>& argv,
-      const process::Subprocess::IO& in,
-      const process::Subprocess::IO& out,
-      const process::Subprocess::IO& err,
+      const mesos::slave::ContainerIO& containerIO,
       const flags::FlagsBase* flags,
       const Option<std::map<std::string, std::string>>& environment,
       const Option<int>& enterNamespaces,
-      const Option<int>& cloneNamespaces) = 0;
+      const Option<int>& cloneNamespaces,
+      const std::vector<int_fd>& whitelistFds) = 0;
 
   // Kill all processes in the containerized context.
   virtual process::Future<Nothing> destroy(const ContainerID& containerId) = 0;
@@ -102,13 +102,12 @@ public:
       const ContainerID& containerId,
       const std::string& path,
       const std::vector<std::string>& argv,
-      const process::Subprocess::IO& in,
-      const process::Subprocess::IO& out,
-      const process::Subprocess::IO& err,
+      const mesos::slave::ContainerIO& containerIO,
       const flags::FlagsBase* flags,
       const Option<std::map<std::string, std::string>>& environment,
       const Option<int>& enterNamespaces,
-      const Option<int>& cloneNamespaces);
+      const Option<int>& cloneNamespaces,
+      const std::vector<int_fd>& whitelistFds);
 
   virtual process::Future<Nothing> destroy(const ContainerID& containerId);
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/f8c8c35a/src/slave/containerizer/mesos/linux_launcher.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/linux_launcher.cpp b/src/slave/containerizer/mesos/linux_launcher.cpp
index 9ce5fbf..3bddcec 100644
--- a/src/slave/containerizer/mesos/linux_launcher.cpp
+++ b/src/slave/containerizer/mesos/linux_launcher.cpp
@@ -73,13 +73,12 @@ public:
       const ContainerID& containerId,
       const string& path,
       const vector<string>& argv,
-      const process::Subprocess::IO& in,
-      const process::Subprocess::IO& out,
-      const process::Subprocess::IO& err,
+      const mesos::slave::ContainerIO& containerIO,
       const flags::FlagsBase* flags,
       const Option<map<string, string>>& environment,
       const Option<int>& enterNamespaces,
-      const Option<int>& cloneNamespaces);
+      const Option<int>& cloneNamespaces,
+      const vector<int_fd>& whitelistFds);
 
   virtual process::Future<Nothing> destroy(const ContainerID& containerId);
 
@@ -249,13 +248,12 @@ Try<pid_t> LinuxLauncher::fork(
     const ContainerID& containerId,
     const string& path,
     const vector<string>& argv,
-    const process::Subprocess::IO& in,
-    const process::Subprocess::IO& out,
-    const process::Subprocess::IO& err,
+    const mesos::slave::ContainerIO& containerIO,
     const flags::FlagsBase* flags,
     const Option<map<string, string>>& environment,
     const Option<int>& enterNamespaces,
-    const Option<int>& cloneNamespaces)
+    const Option<int>& cloneNamespaces,
+    const vector<int_fd>& whitelistFds)
 {
   return dispatch(
       process.get(),
@@ -263,13 +261,12 @@ Try<pid_t> LinuxLauncher::fork(
       containerId,
       path,
       argv,
-      in,
-      out,
-      err,
+      containerIO,
       flags,
       environment,
       enterNamespaces,
-      cloneNamespaces).get();
+      cloneNamespaces,
+      whitelistFds).get();
 }
 
 
@@ -463,13 +460,12 @@ Try<pid_t> LinuxLauncherProcess::fork(
     const ContainerID& containerId,
     const string& path,
     const vector<string>& argv,
-    const process::Subprocess::IO& in,
-    const process::Subprocess::IO& out,
-    const process::Subprocess::IO& err,
+    const mesos::slave::ContainerIO& containerIO,
     const flags::FlagsBase* flags,
     const Option<map<string, string>>& environment,
     const Option<int>& enterNamespaces,
-    const Option<int>& cloneNamespaces)
+    const Option<int>& cloneNamespaces,
+    const vector<int_fd>& whitelistFds)
 {
   // Make sure this container (nested or not) is unique.
   if (containers.contains(containerId)) {
@@ -544,9 +540,9 @@ Try<pid_t> LinuxLauncherProcess::fork(
   Try<Subprocess> child = subprocess(
       path,
       argv,
-      in,
-      out,
-      err,
+      containerIO.in,
+      containerIO.out,
+      containerIO.err,
       flags,
       environment,
       [target, enterFlags, cloneFlags](const lambda::function<int()>& child) {

http://git-wip-us.apache.org/repos/asf/mesos/blob/f8c8c35a/src/slave/containerizer/mesos/linux_launcher.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/linux_launcher.hpp b/src/slave/containerizer/mesos/linux_launcher.hpp
index 4c5cdd1..a542d43 100644
--- a/src/slave/containerizer/mesos/linux_launcher.hpp
+++ b/src/slave/containerizer/mesos/linux_launcher.hpp
@@ -52,13 +52,12 @@ public:
       const ContainerID& containerId,
       const std::string& path,
       const std::vector<std::string>& argv,
-      const process::Subprocess::IO& in,
-      const process::Subprocess::IO& out,
-      const process::Subprocess::IO& err,
+      const mesos::slave::ContainerIO& containerIO,
       const flags::FlagsBase* flags,
       const Option<std::map<std::string, std::string>>& environment,
       const Option<int>& enterNamespaces,
-      const Option<int>& cloneNamespaces);
+      const Option<int>& cloneNamespaces,
+      const std::vector<int_fd>& whitelistFds);
 
   virtual process::Future<Nothing> destroy(const ContainerID& containerId);
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/f8c8c35a/src/tests/containerizer/launcher.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/launcher.cpp b/src/tests/containerizer/launcher.cpp
index a92d989..51ae4f9 100644
--- a/src/tests/containerizer/launcher.cpp
+++ b/src/tests/containerizer/launcher.cpp
@@ -30,7 +30,7 @@ ACTION_P(InvokeRecover, launcher)
 ACTION_P(InvokeFork, launcher)
 {
   return launcher->real->fork(
-      arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+      arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
 }
 
 
@@ -51,9 +51,9 @@ TestLauncher::TestLauncher(const process::Owned<slave::Launcher>& _real)
   EXPECT_CALL(*this, recover(_))
     .WillRepeatedly(DoDefault());
 
-  ON_CALL(*this, fork(_, _, _, _, _, _, _, _, _, _))
+  ON_CALL(*this, fork(_, _, _, _, _, _, _, _, _))
     .WillByDefault(InvokeFork(this));
-  EXPECT_CALL(*this, fork(_, _, _, _, _, _, _, _, _, _))
+  EXPECT_CALL(*this, fork(_, _, _, _, _, _, _, _, _))
     .WillRepeatedly(DoDefault());
 
   ON_CALL(*this, destroy(_))

http://git-wip-us.apache.org/repos/asf/mesos/blob/f8c8c35a/src/tests/containerizer/launcher.hpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/launcher.hpp b/src/tests/containerizer/launcher.hpp
index 776cb35..40728e0 100644
--- a/src/tests/containerizer/launcher.hpp
+++ b/src/tests/containerizer/launcher.hpp
@@ -55,19 +55,18 @@ public:
       process::Future<hashset<ContainerID>>(
           const std::vector<mesos::slave::ContainerState>& states));
 
-  MOCK_METHOD10(
+  MOCK_METHOD9(
       fork,
       Try<pid_t>(
           const ContainerID& containerId,
           const std::string& path,
           const std::vector<std::string>& argv,
-          const process::Subprocess::IO& in,
-          const process::Subprocess::IO& out,
-          const process::Subprocess::IO& err,
+          const mesos::slave::ContainerIO& containerIO,
           const flags::FlagsBase* flags,
           const Option<std::map<std::string, std::string>>& env,
           const Option<int>& enterNamespaces,
-          const Option<int>& cloneNamespaces));
+          const Option<int>& cloneNamespaces,
+          const std::vector<int_fd>& whitelistFds));
 
   MOCK_METHOD1(
       destroy,

http://git-wip-us.apache.org/repos/asf/mesos/blob/f8c8c35a/src/tests/containerizer/mesos_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/mesos_containerizer_tests.cpp b/src/tests/containerizer/mesos_containerizer_tests.cpp
index 362d9fb..1b8e53c 100644
--- a/src/tests/containerizer/mesos_containerizer_tests.cpp
+++ b/src/tests/containerizer/mesos_containerizer_tests.cpp
@@ -1323,13 +1323,12 @@ TEST_F(MesosLauncherStatusTest, ExecutorPIDTest)
       containerId,
       path::join(flags.launcher_dir, MESOS_CONTAINERIZER),
       vector<string>(),
-      Subprocess::FD(STDIN_FILENO),
-      Subprocess::FD(STDOUT_FILENO),
-      Subprocess::FD(STDERR_FILENO),
+      mesos::slave::ContainerIO(),
       nullptr,
       None(),
       None(),
-      None());
+      None(),
+      vector<int_fd>());
 
   ASSERT_SOME(forked);