From cd209b34e39a29d21f13e526f8a869c608b1ff17 Mon Sep 17 00:00:00 2001 From: Rocky Liu Date: Tue, 13 May 2014 16:40:54 -0700 Subject: [PATCH] Revert "[folly::Subprocess] Set O_CLOEXEC by default when creating pipes to avoid race conditions resulting from concurrent Subprocess creations" Summary: This reverts commit c2f089cf080f2b3effa9efa5e4708b9674437d45. Test Plan: Compile && folly::Subprocess unit tests Reviewed By: tudorb@fb.com FB internal diff: D1327952 --- folly/Subprocess.cpp | 40 +++++++++++++---------------------- folly/test/SubprocessTest.cpp | 20 ------------------ 2 files changed, 15 insertions(+), 45 deletions(-) diff --git a/folly/Subprocess.cpp b/folly/Subprocess.cpp index 54f614b5..b1dd0b35 100644 --- a/folly/Subprocess.cpp +++ b/folly/Subprocess.cpp @@ -252,24 +252,24 @@ void Subprocess::spawn( }); // Create a pipe to use to receive error information from the child, - // in case it fails before calling exec(), setting the close-on-exec flag - // on both sides of the pipe. - // This way the pipe will be closed automatically in the child if execve() - // succeeds. If the exec fails the child can write error information to the - // pipe. - // Note that O_CLOEXEC must be set in a single call while we are creating - // the pipe instead of doing pipe()/fcntl separately, which might race if a - // another thread calls fork()/exec() concurrently and both sides of the pipe - // may be inherited by the corresponding child process without being closed. + // in case it fails before calling exec() int errFds[2]; - int r = ::pipe2(errFds, O_CLOEXEC); - checkUnixError(r, "pipe2"); + int r = ::pipe(errFds); + checkUnixError(r, "pipe"); SCOPE_EXIT { CHECK_ERR(::close(errFds[0])); if (errFds[1] >= 0) { CHECK_ERR(::close(errFds[1])); } }; + // Ask the child to close the read end of the error pipe. + options.fdActions_[errFds[0]] = CLOSE; + // Set the close-on-exec flag on the write side of the pipe. + // This way the pipe will be closed automatically in the child if execve() + // succeeds. If the exec fails the child can write error information to the + // pipe. + r = fcntl(errFds[1], F_SETFD, FD_CLOEXEC); + checkUnixError(r, "set FD_CLOEXEC"); // Perform the actual work of setting up pipes then forking and // executing the child. @@ -316,14 +316,8 @@ void Subprocess::spawnInternal( for (auto& p : options.fdActions_) { if (p.second == PIPE_IN || p.second == PIPE_OUT) { int fds[2]; - // Set O_CLOEXEC on both ends of the pipe atomically while creating - // the pipe. The child will clear O_CLOEXEC on its side of the pipe - // before calling exec() so that stays open afterwards. - // This way even if a concurrently constructed Subprocess inherits - // both ends of this pipe, they will be automatically closed - // after the corresponding exec(). - r = ::pipe2(fds, O_CLOEXEC); - checkUnixError(r, "pipe2"); + r = ::pipe(fds); + checkUnixError(r, "pipe"); PipeInfo pinfo; pinfo.direction = p.second; int cfd; @@ -432,13 +426,9 @@ int Subprocess::prepareChild(const Options& options, } } + // Close parent's ends of all pipes for (auto& p : pipes_) { - // Clear FD_CLOEXEC on the child side of the pipe so - // it stays open after exec() (so that the child could - // access it). - // See spawnInternal() for why FD_CLOEXEC must be set - // by default on pipes. - r = fcntl(p.childFd, F_SETFD, 0); + r = ::close(p.parentFd); if (r == -1) { return errno; } diff --git a/folly/test/SubprocessTest.cpp b/folly/test/SubprocessTest.cpp index b81a1bf1..098b66b3 100644 --- a/folly/test/SubprocessTest.cpp +++ b/folly/test/SubprocessTest.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -33,7 +32,6 @@ #include "folly/gen/File.h" #include "folly/gen/String.h" #include "folly/experimental/io/FsUtil.h" -#include "folly/Memory.h" using namespace folly; @@ -438,21 +436,3 @@ TEST(CommunicateSubprocessTest, Chatty) { EXPECT_EQ(0, proc.wait().exitStatus()); }); } - -TEST(ConcurrentSubprocessTest, construction) { - std::vector> ps(100); - auto action = [](std::unique_ptr& p) { - p = make_unique("read", Subprocess::pipeStdout()); - }; - std::vector threads; - for (auto& p : ps) { - threads.emplace_back(action, ref(p)); - } - for (auto& t : threads) { - t.join(); - } - for (auto& p : ps) { - p->terminate(); - p->wait(); - } -} -- 2.34.1