Support: Add a utility to remap std{in,out,err} to /dev/null if closed

It's possible to start a program with one (or all) of the standard file
descriptors closed.  Subsequent open system calls will give the program
a low-numbered file descriptor.

This is problematic because we may believe we are writing to standard
out instead of a file.

Introduce Process::FixupStandardFileDescriptors, a helper function to
remap standard file descriptors to /dev/null if they were closed before
the program started.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@219170 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
David Majnemer 2014-10-06 23:16:18 +00:00
parent 5b8cd15092
commit 3ba3a4ccfd
3 changed files with 69 additions and 0 deletions

View File

@ -186,6 +186,12 @@ public:
ArrayRef<const char *> ArgsFromMain,
SpecificBumpPtrAllocator<char> &ArgAllocator);
// This functions ensures that the standard file descriptors (input, output,
// and error) are properly mapped to a file descriptor before we use any of
// them. This should only be called by standalone programs, library
// components should not call this.
static std::error_code FixupStandardFileDescriptors();
/// This function determines if the standard input is connected directly
/// to a user's input (keyboard probably), rather than coming from a file
/// or pipe.

View File

@ -18,6 +18,9 @@
#include "llvm/Support/Mutex.h"
#include "llvm/Support/MutexGuard.h"
#include "llvm/Support/TimeValue.h"
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
@ -199,6 +202,62 @@ Process::GetArgumentVector(SmallVectorImpl<const char *> &ArgsOut,
return std::error_code();
}
namespace {
class FDCloser {
public:
FDCloser(int &FD) : FD(FD), KeepOpen(false) {}
void keepOpen() { KeepOpen = true; }
~FDCloser() {
if (!KeepOpen && FD >= 0)
::close(FD);
}
private:
FDCloser(const FDCloser &) LLVM_DELETED_FUNCTION;
void operator=(const FDCloser &) LLVM_DELETED_FUNCTION;
int &FD;
bool KeepOpen;
};
}
std::error_code Process::FixupStandardFileDescriptors() {
int NullFD = -1;
FDCloser FDC(NullFD);
const int StandardFDs[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
for (int StandardFD : StandardFDs) {
struct stat st;
errno = 0;
while (fstat(StandardFD, &st) < 0) {
assert(errno && "expected errno to be set if fstat failed!");
// fstat should return EBADF if the file descriptor is closed.
if (errno == EBADF)
break;
// retry fstat if we got EINTR, otherwise bubble up the failure.
if (errno != EINTR)
return std::error_code(errno, std::generic_category());
}
// if fstat succeeds, move on to the next FD.
if (!errno)
continue;
assert(errno == EBADF && "expected errno to have EBADF at this point!");
if (NullFD < 0) {
while ((NullFD = open("/dev/null", O_RDWR)) < 0) {
if (errno == EINTR)
continue;
return std::error_code(errno, std::generic_category());
}
}
if (NullFD == StandardFD)
FDC.keepOpen();
else if (dup2(NullFD, StandardFD) < 0)
return std::error_code(errno, std::generic_category());
}
return std::error_code();
}
bool Process::StandardInIsUserInput() {
return FileDescriptorIsDisplayed(STDIN_FILENO);
}

View File

@ -273,6 +273,10 @@ Process::GetArgumentVector(SmallVectorImpl<const char *> &Args,
return ec;
}
std::error_code Process::FixupStandardFileDescriptors() {
return std::error_code();
}
bool Process::StandardInIsUserInput() {
return FileDescriptorIsDisplayed(0);
}