Skip to content

Commit fd5be16

Browse files
authored
Merge pull request #154 from apple/cherry-pick-34091610
Cherry pick 34091610
2 parents b5f42f8 + 3e1d305 commit fd5be16

7 files changed

+118
-93
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_mac.cc

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ extern "C" {
6464
#include <pthread.h>
6565
#include <sched.h>
6666
#include <signal.h>
67+
#include <spawn.h>
6768
#include <stdlib.h>
69+
#include <sys/ioctl.h>
6870
#include <sys/mman.h>
6971
#include <sys/resource.h>
7072
#include <sys/stat.h>
@@ -239,27 +241,102 @@ int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
239241
(size_t)newlen);
240242
}
241243

242-
int internal_forkpty(int *aparent) {
243-
int parent, worker;
244-
if (openpty(&parent, &worker, nullptr, nullptr, nullptr) == -1) return -1;
245-
int pid = internal_fork();
246-
if (pid == -1) {
247-
close(parent);
248-
close(worker);
249-
return -1;
244+
static fd_t internal_spawn_impl(const char *argv[], pid_t *pid) {
245+
fd_t master_fd = kInvalidFd;
246+
fd_t slave_fd = kInvalidFd;
247+
248+
auto fd_closer = at_scope_exit([&] {
249+
internal_close(master_fd);
250+
internal_close(slave_fd);
251+
});
252+
253+
// We need a new pseudoterminal to avoid buffering problems. The 'atos' tool
254+
// in particular detects when it's talking to a pipe and forgets to flush the
255+
// output stream after sending a response.
256+
master_fd = posix_openpt(O_RDWR);
257+
if (master_fd == kInvalidFd) return kInvalidFd;
258+
259+
int res = grantpt(master_fd) || unlockpt(master_fd);
260+
if (res != 0) return kInvalidFd;
261+
262+
// Use TIOCPTYGNAME instead of ptsname() to avoid threading problems.
263+
char slave_pty_name[128];
264+
res = ioctl(master_fd, TIOCPTYGNAME, slave_pty_name);
265+
if (res == -1) return kInvalidFd;
266+
267+
slave_fd = internal_open(slave_pty_name, O_RDWR);
268+
if (slave_fd == kInvalidFd) return kInvalidFd;
269+
270+
// File descriptor actions
271+
posix_spawn_file_actions_t acts;
272+
res = posix_spawn_file_actions_init(&acts);
273+
if (res != 0) return kInvalidFd;
274+
275+
auto acts_cleanup = at_scope_exit([&] {
276+
posix_spawn_file_actions_destroy(&acts);
277+
});
278+
279+
res = posix_spawn_file_actions_adddup2(&acts, slave_fd, STDIN_FILENO) ||
280+
posix_spawn_file_actions_adddup2(&acts, slave_fd, STDOUT_FILENO) ||
281+
posix_spawn_file_actions_addclose(&acts, slave_fd);
282+
if (res != 0) return kInvalidFd;
283+
284+
// Spawn attributes
285+
posix_spawnattr_t attrs;
286+
res = posix_spawnattr_init(&attrs);
287+
if (res != 0) return kInvalidFd;
288+
289+
auto attrs_cleanup = at_scope_exit([&] {
290+
posix_spawnattr_destroy(&attrs);
291+
});
292+
293+
// In the spawned process, close all file descriptors that are not explicitly
294+
// described by the file actions object. This is Darwin-specific extension.
295+
res = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_CLOEXEC_DEFAULT);
296+
if (res != 0) return kInvalidFd;
297+
298+
// posix_spawn
299+
char **argv_casted = const_cast<char **>(argv);
300+
char **env = GetEnviron();
301+
res = posix_spawn(pid, argv[0], &acts, &attrs, argv_casted, env);
302+
if (res != 0) return kInvalidFd;
303+
304+
// Disable echo in the new terminal, disable CR.
305+
struct termios termflags;
306+
tcgetattr(master_fd, &termflags);
307+
termflags.c_oflag &= ~ONLCR;
308+
termflags.c_lflag &= ~ECHO;
309+
tcsetattr(master_fd, TCSANOW, &termflags);
310+
311+
// On success, do not close master_fd on scope exit.
312+
fd_t fd = master_fd;
313+
master_fd = kInvalidFd;
314+
315+
return fd;
316+
}
317+
318+
fd_t internal_spawn(const char *argv[], pid_t *pid) {
319+
// The client program may close its stdin and/or stdout and/or stderr thus
320+
// allowing open/posix_openpt to reuse file descriptors 0, 1 or 2. In this
321+
// case the communication is broken if either the parent or the child tries to
322+
// close or duplicate these descriptors. We temporarily reserve these
323+
// descriptors here to prevent this.
324+
fd_t low_fds[3];
325+
size_t count = 0;
326+
327+
for (; count < 3; count++) {
328+
low_fds[count] = posix_openpt(O_RDWR);
329+
if (low_fds[count] >= STDERR_FILENO)
330+
break;
250331
}
251-
if (pid == 0) {
252-
close(parent);
253-
if (login_tty(worker) != 0) {
254-
// We already forked, there's not much we can do. Let's quit.
255-
Report("login_tty failed (errno %d)\n", errno);
256-
internal__exit(1);
257-
}
258-
} else {
259-
*aparent = parent;
260-
close(worker);
332+
333+
fd_t fd = internal_spawn_impl(argv, pid);
334+
335+
for (; count > 0; count--) {
336+
internal_close(low_fds[count]);
261337
}
262-
return pid;
338+
339+
return fd;
263340
}
264341

265342
uptr internal_rename(const char *oldpath, const char *newpath) {

compiler-rt/lib/sanitizer_common/sanitizer_posix.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ uptr internal_ptrace(int request, int pid, void *addr, void *data);
5959
uptr internal_waitpid(int pid, int *status, int options);
6060

6161
int internal_fork();
62-
int internal_forkpty(int *amaster);
62+
fd_t internal_spawn(const char *argv[], pid_t *pid);
6363

6464
int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
6565
uptr *oldlenp, const void *newp, uptr newlen);

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_internal.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class SymbolizerTool {
7171
// SymbolizerProcess may not be used from two threads simultaneously.
7272
class SymbolizerProcess {
7373
public:
74-
explicit SymbolizerProcess(const char *path, bool use_forkpty = false);
74+
explicit SymbolizerProcess(const char *path, bool use_posix_spawn = false);
7575
const char *SendCommand(const char *command);
7676

7777
protected:
@@ -108,7 +108,7 @@ class SymbolizerProcess {
108108
uptr times_restarted_;
109109
bool failed_to_start_;
110110
bool reported_invalid_path_;
111-
bool use_forkpty_;
111+
bool use_posix_spawn_;
112112
};
113113

114114
class LLVMSymbolizerProcess;

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,14 +389,14 @@ const char *LLVMSymbolizer::FormatAndSendCommand(bool is_data,
389389
return symbolizer_process_->SendCommand(buffer_);
390390
}
391391

392-
SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty)
392+
SymbolizerProcess::SymbolizerProcess(const char *path, bool use_posix_spawn)
393393
: path_(path),
394394
input_fd_(kInvalidFd),
395395
output_fd_(kInvalidFd),
396396
times_restarted_(0),
397397
failed_to_start_(false),
398398
reported_invalid_path_(false),
399-
use_forkpty_(use_forkpty) {
399+
use_posix_spawn_(use_posix_spawn) {
400400
CHECK(path_);
401401
CHECK_NE(path_[0], '\0');
402402
}

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
5050
class AtosSymbolizerProcess : public SymbolizerProcess {
5151
public:
5252
explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
53-
: SymbolizerProcess(path, /*use_forkpty*/ true) {
53+
: SymbolizerProcess(path, /*use_posix_spawn*/ true) {
5454
// Put the string command line argument in the object so that it outlives
5555
// the call to GetArgV.
5656
internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc

Lines changed: 15 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@
3333
#include <sys/wait.h>
3434
#include <unistd.h>
3535

36-
#if SANITIZER_MAC
37-
#include <util.h> // for forkpty()
38-
#endif // SANITIZER_MAC
39-
4036
// C++ demangling function, as required by Itanium C++ ABI. This is weak,
4137
// because we do not require a C++ ABI library to be linked to a program
4238
// using sanitizers; if it's not present, we'll just use the mangled name.
@@ -151,80 +147,32 @@ bool SymbolizerProcess::StartSymbolizerSubprocess() {
151147
return false;
152148
}
153149

154-
int pid = -1;
155-
156-
int infd[2];
157-
internal_memset(&infd, 0, sizeof(infd));
158-
int outfd[2];
159-
internal_memset(&outfd, 0, sizeof(outfd));
160-
if (!CreateTwoHighNumberedPipes(infd, outfd)) {
161-
Report("WARNING: Can't create a socket pair to start "
162-
"external symbolizer (errno: %d)\n", errno);
163-
return false;
164-
}
150+
const char *argv[kArgVMax];
151+
GetArgV(path_, argv);
152+
pid_t pid;
165153

166-
if (use_forkpty_) {
154+
if (use_posix_spawn_) {
167155
#if SANITIZER_MAC
168-
fd_t fd = kInvalidFd;
169-
170-
// forkpty redirects stdout and stderr into a single stream, so we would
171-
// receive error messages as standard replies. To avoid that, let's dup
172-
// stderr and restore it in the child.
173-
int saved_stderr = dup(STDERR_FILENO);
174-
CHECK_GE(saved_stderr, 0);
175-
176-
// We only need one pipe, for stdin of the child.
177-
close(outfd[0]);
178-
close(outfd[1]);
179-
180-
// Use forkpty to disable buffering in the new terminal.
181-
pid = internal_forkpty(&fd);
182-
if (pid == -1) {
183-
// forkpty() failed.
184-
Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
156+
fd_t fd = internal_spawn(argv, &pid);
157+
if (fd == kInvalidFd) {
158+
Report("WARNING: failed to spawn external symbolizer (errno: %d)\n",
185159
errno);
186160
return false;
187-
} else if (pid == 0) {
188-
// Child subprocess.
189-
190-
// infd[0] is the child's reading end.
191-
close(infd[1]);
192-
193-
// Set up stdin to read from the pipe.
194-
CHECK_GE(dup2(infd[0], STDIN_FILENO), 0);
195-
close(infd[0]);
196-
197-
// Restore stderr.
198-
CHECK_GE(dup2(saved_stderr, STDERR_FILENO), 0);
199-
close(saved_stderr);
200-
201-
const char *argv[kArgVMax];
202-
GetArgV(path_, argv);
203-
execv(path_, const_cast<char **>(&argv[0]));
204-
internal__exit(1);
205161
}
206162

207-
// Input for the child, infd[1] is our writing end.
208-
output_fd_ = infd[1];
209-
close(infd[0]);
210-
211-
// Continue execution in parent process.
212163
input_fd_ = fd;
213-
214-
close(saved_stderr);
215-
216-
// Disable echo in the new terminal, disable CR.
217-
struct termios termflags;
218-
tcgetattr(fd, &termflags);
219-
termflags.c_oflag &= ~ONLCR;
220-
termflags.c_lflag &= ~ECHO;
221-
tcsetattr(fd, TCSANOW, &termflags);
164+
output_fd_ = fd;
222165
#else // SANITIZER_MAC
223166
UNIMPLEMENTED();
224167
#endif // SANITIZER_MAC
225168
} else {
226-
const char *argv[kArgVMax];
227-
GetArgV(path_, argv);
169+
fd_t infd[2] = {}, outfd[2] = {};
170+
if (!CreateTwoHighNumberedPipes(infd, outfd)) {
171+
Report("WARNING: Can't create a socket pair to start "
172+
"external symbolizer (errno: %d)\n", errno);
173+
return false;
174+
}
175+
228176
pid = StartSubprocess(path_, argv, /* stdin */ outfd[0],
229177
/* stdout */ infd[1]);
230178
if (pid < 0) {

compiler-rt/test/asan/TestCases/Darwin/dladdr-demangling.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class MyClass {
1919
// CHECK-DLADDR: Using dladdr symbolizer
2020
// CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
2121
// CHECK: {{READ of size 1 at 0x.* thread T0}}
22-
// CHECK-DLADDR: failed to fork
22+
// CHECK-DLADDR: failed to spawn external symbolizer
2323
// CHECK: {{ #0 0x.* in MyClass::my_function\(int\)}}
2424
// CHECK: {{freed by thread T0 here:}}
2525
// CHECK: {{ #0 0x.* in wrap_free}}

0 commit comments

Comments
 (0)