Skip to content

Cherry pick 34091610 #154

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 96 additions & 19 deletions compiler-rt/lib/sanitizer_common/sanitizer_mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ extern "C" {
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <spawn.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/stat.h>
Expand Down Expand Up @@ -239,27 +241,102 @@ int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
(size_t)newlen);
}

int internal_forkpty(int *aparent) {
int parent, worker;
if (openpty(&parent, &worker, nullptr, nullptr, nullptr) == -1) return -1;
int pid = internal_fork();
if (pid == -1) {
close(parent);
close(worker);
return -1;
static fd_t internal_spawn_impl(const char *argv[], pid_t *pid) {
fd_t master_fd = kInvalidFd;
fd_t slave_fd = kInvalidFd;

auto fd_closer = at_scope_exit([&] {
internal_close(master_fd);
internal_close(slave_fd);
});

// We need a new pseudoterminal to avoid buffering problems. The 'atos' tool
// in particular detects when it's talking to a pipe and forgets to flush the
// output stream after sending a response.
master_fd = posix_openpt(O_RDWR);
if (master_fd == kInvalidFd) return kInvalidFd;

int res = grantpt(master_fd) || unlockpt(master_fd);
if (res != 0) return kInvalidFd;

// Use TIOCPTYGNAME instead of ptsname() to avoid threading problems.
char slave_pty_name[128];
res = ioctl(master_fd, TIOCPTYGNAME, slave_pty_name);
if (res == -1) return kInvalidFd;

slave_fd = internal_open(slave_pty_name, O_RDWR);
if (slave_fd == kInvalidFd) return kInvalidFd;

// File descriptor actions
posix_spawn_file_actions_t acts;
res = posix_spawn_file_actions_init(&acts);
if (res != 0) return kInvalidFd;

auto acts_cleanup = at_scope_exit([&] {
posix_spawn_file_actions_destroy(&acts);
});

res = posix_spawn_file_actions_adddup2(&acts, slave_fd, STDIN_FILENO) ||
posix_spawn_file_actions_adddup2(&acts, slave_fd, STDOUT_FILENO) ||
posix_spawn_file_actions_addclose(&acts, slave_fd);
if (res != 0) return kInvalidFd;

// Spawn attributes
posix_spawnattr_t attrs;
res = posix_spawnattr_init(&attrs);
if (res != 0) return kInvalidFd;

auto attrs_cleanup = at_scope_exit([&] {
posix_spawnattr_destroy(&attrs);
});

// In the spawned process, close all file descriptors that are not explicitly
// described by the file actions object. This is Darwin-specific extension.
res = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_CLOEXEC_DEFAULT);
if (res != 0) return kInvalidFd;

// posix_spawn
char **argv_casted = const_cast<char **>(argv);
char **env = GetEnviron();
res = posix_spawn(pid, argv[0], &acts, &attrs, argv_casted, env);
if (res != 0) return kInvalidFd;

// Disable echo in the new terminal, disable CR.
struct termios termflags;
tcgetattr(master_fd, &termflags);
termflags.c_oflag &= ~ONLCR;
termflags.c_lflag &= ~ECHO;
tcsetattr(master_fd, TCSANOW, &termflags);

// On success, do not close master_fd on scope exit.
fd_t fd = master_fd;
master_fd = kInvalidFd;

return fd;
}

fd_t internal_spawn(const char *argv[], pid_t *pid) {
// The client program may close its stdin and/or stdout and/or stderr thus
// allowing open/posix_openpt to reuse file descriptors 0, 1 or 2. In this
// case the communication is broken if either the parent or the child tries to
// close or duplicate these descriptors. We temporarily reserve these
// descriptors here to prevent this.
fd_t low_fds[3];
size_t count = 0;

for (; count < 3; count++) {
low_fds[count] = posix_openpt(O_RDWR);
if (low_fds[count] >= STDERR_FILENO)
break;
}
if (pid == 0) {
close(parent);
if (login_tty(worker) != 0) {
// We already forked, there's not much we can do. Let's quit.
Report("login_tty failed (errno %d)\n", errno);
internal__exit(1);
}
} else {
*aparent = parent;
close(worker);

fd_t fd = internal_spawn_impl(argv, pid);

for (; count > 0; count--) {
internal_close(low_fds[count]);
}
return pid;

return fd;
}

uptr internal_rename(const char *oldpath, const char *newpath) {
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/sanitizer_common/sanitizer_posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ uptr internal_ptrace(int request, int pid, void *addr, void *data);
uptr internal_waitpid(int pid, int *status, int options);

int internal_fork();
int internal_forkpty(int *amaster);
fd_t internal_spawn(const char *argv[], pid_t *pid);

int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
uptr *oldlenp, const void *newp, uptr newlen);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class SymbolizerTool {
// SymbolizerProcess may not be used from two threads simultaneously.
class SymbolizerProcess {
public:
explicit SymbolizerProcess(const char *path, bool use_forkpty = false);
explicit SymbolizerProcess(const char *path, bool use_posix_spawn = false);
const char *SendCommand(const char *command);

protected:
Expand Down Expand Up @@ -108,7 +108,7 @@ class SymbolizerProcess {
uptr times_restarted_;
bool failed_to_start_;
bool reported_invalid_path_;
bool use_forkpty_;
bool use_posix_spawn_;
};

class LLVMSymbolizerProcess;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,14 +389,14 @@ const char *LLVMSymbolizer::FormatAndSendCommand(bool is_data,
return symbolizer_process_->SendCommand(buffer_);
}

SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty)
SymbolizerProcess::SymbolizerProcess(const char *path, bool use_posix_spawn)
: path_(path),
input_fd_(kInvalidFd),
output_fd_(kInvalidFd),
times_restarted_(0),
failed_to_start_(false),
reported_invalid_path_(false),
use_forkpty_(use_forkpty) {
use_posix_spawn_(use_posix_spawn) {
CHECK(path_);
CHECK_NE(path_[0], '\0');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
class AtosSymbolizerProcess : public SymbolizerProcess {
public:
explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
: SymbolizerProcess(path, /*use_forkpty*/ true) {
: SymbolizerProcess(path, /*use_posix_spawn*/ true) {
// Put the string command line argument in the object so that it outlives
// the call to GetArgV.
internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@
#include <sys/wait.h>
#include <unistd.h>

#if SANITIZER_MAC
#include <util.h> // for forkpty()
#endif // SANITIZER_MAC

// C++ demangling function, as required by Itanium C++ ABI. This is weak,
// because we do not require a C++ ABI library to be linked to a program
// using sanitizers; if it's not present, we'll just use the mangled name.
Expand Down Expand Up @@ -151,80 +147,32 @@ bool SymbolizerProcess::StartSymbolizerSubprocess() {
return false;
}

int pid = -1;

int infd[2];
internal_memset(&infd, 0, sizeof(infd));
int outfd[2];
internal_memset(&outfd, 0, sizeof(outfd));
if (!CreateTwoHighNumberedPipes(infd, outfd)) {
Report("WARNING: Can't create a socket pair to start "
"external symbolizer (errno: %d)\n", errno);
return false;
}
const char *argv[kArgVMax];
GetArgV(path_, argv);
pid_t pid;

if (use_forkpty_) {
if (use_posix_spawn_) {
#if SANITIZER_MAC
fd_t fd = kInvalidFd;

// forkpty redirects stdout and stderr into a single stream, so we would
// receive error messages as standard replies. To avoid that, let's dup
// stderr and restore it in the child.
int saved_stderr = dup(STDERR_FILENO);
CHECK_GE(saved_stderr, 0);

// We only need one pipe, for stdin of the child.
close(outfd[0]);
close(outfd[1]);

// Use forkpty to disable buffering in the new terminal.
pid = internal_forkpty(&fd);
if (pid == -1) {
// forkpty() failed.
Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
fd_t fd = internal_spawn(argv, &pid);
if (fd == kInvalidFd) {
Report("WARNING: failed to spawn external symbolizer (errno: %d)\n",
errno);
return false;
} else if (pid == 0) {
// Child subprocess.

// infd[0] is the child's reading end.
close(infd[1]);

// Set up stdin to read from the pipe.
CHECK_GE(dup2(infd[0], STDIN_FILENO), 0);
close(infd[0]);

// Restore stderr.
CHECK_GE(dup2(saved_stderr, STDERR_FILENO), 0);
close(saved_stderr);

const char *argv[kArgVMax];
GetArgV(path_, argv);
execv(path_, const_cast<char **>(&argv[0]));
internal__exit(1);
}

// Input for the child, infd[1] is our writing end.
output_fd_ = infd[1];
close(infd[0]);

// Continue execution in parent process.
input_fd_ = fd;

close(saved_stderr);

// Disable echo in the new terminal, disable CR.
struct termios termflags;
tcgetattr(fd, &termflags);
termflags.c_oflag &= ~ONLCR;
termflags.c_lflag &= ~ECHO;
tcsetattr(fd, TCSANOW, &termflags);
output_fd_ = fd;
#else // SANITIZER_MAC
UNIMPLEMENTED();
#endif // SANITIZER_MAC
} else {
const char *argv[kArgVMax];
GetArgV(path_, argv);
fd_t infd[2] = {}, outfd[2] = {};
if (!CreateTwoHighNumberedPipes(infd, outfd)) {
Report("WARNING: Can't create a socket pair to start "
"external symbolizer (errno: %d)\n", errno);
return false;
}

pid = StartSubprocess(path_, argv, /* stdin */ outfd[0],
/* stdout */ infd[1]);
if (pid < 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class MyClass {
// CHECK-DLADDR: Using dladdr symbolizer
// CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
// CHECK: {{READ of size 1 at 0x.* thread T0}}
// CHECK-DLADDR: failed to fork
// CHECK-DLADDR: failed to spawn external symbolizer
// CHECK: {{ #0 0x.* in MyClass::my_function\(int\)}}
// CHECK: {{freed by thread T0 here:}}
// CHECK: {{ #0 0x.* in wrap_free}}
Expand Down