Skip to content

Commit dc92bd0

Browse files
authored
[sanitizer_common] [Darwin] Replace pty with pipe on posix_spawn path for spawning symbolizer (#170809)
Due to a legacy incompatibility with `atos`, we were allocating a pty whenever we spawned the symbolizer. This is no longer necessary and we can use a regular ol' pipe. This PR is split into two commits: - The first removes the pty allocation and replaces it with a pipe. This relocates the `CreateTwoHighNumberedPipes` call to be common to the `posix_spawn` and `StartSubprocess` path. - The second commit adds the `child_stdin_fd_` field to `SymbolizerProcess`, storing the read end of the stdin pipe. By holding on to this fd for the lifetime of the symbolizer, we are able to avoid getting SIGPIPE (which would occur when we write to a pipe whose read-end had been closed due to the death of the symbolizer). This will be very close to solving #120915, but this PR is intentionally not touching the non-posix_spawn path. rdar://165894284
1 parent 4bff9fd commit dc92bd0

File tree

5 files changed

+65
-90
lines changed

5 files changed

+65
-90
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp

Lines changed: 27 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -281,53 +281,43 @@ int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
281281
(size_t)newlen);
282282
}
283283

284-
static fd_t internal_spawn_impl(const char *argv[], const char *envp[],
285-
pid_t *pid) {
286-
fd_t primary_fd = kInvalidFd;
287-
fd_t secondary_fd = kInvalidFd;
288-
284+
bool internal_spawn(const char* argv[], const char* envp[], pid_t* pid,
285+
fd_t fd_stdin, fd_t fd_stdout) {
286+
// NOTE: Caller ensures that fd_stdin and fd_stdout are not 0, 1, or 2, since
287+
// this can break communication.
288+
//
289+
// NOTE: Caller is responsible for closing fd_stdin after the process has
290+
// died.
291+
292+
int res;
289293
auto fd_closer = at_scope_exit([&] {
290-
internal_close(primary_fd);
291-
internal_close(secondary_fd);
294+
// NOTE: We intentionally do not close fd_stdin since this can
295+
// cause us to receive a fatal SIGPIPE if the process dies.
296+
internal_close(fd_stdout);
292297
});
293298

294-
// We need a new pseudoterminal to avoid buffering problems. The 'atos' tool
295-
// in particular detects when it's talking to a pipe and forgets to flush the
296-
// output stream after sending a response.
297-
primary_fd = posix_openpt(O_RDWR);
298-
if (primary_fd == kInvalidFd)
299-
return kInvalidFd;
300-
301-
int res = grantpt(primary_fd) || unlockpt(primary_fd);
302-
if (res != 0) return kInvalidFd;
303-
304-
// Use TIOCPTYGNAME instead of ptsname() to avoid threading problems.
305-
char secondary_pty_name[128];
306-
res = ioctl(primary_fd, TIOCPTYGNAME, secondary_pty_name);
307-
if (res == -1) return kInvalidFd;
308-
309-
secondary_fd = internal_open(secondary_pty_name, O_RDWR);
310-
if (secondary_fd == kInvalidFd)
311-
return kInvalidFd;
312-
313299
// File descriptor actions
314300
posix_spawn_file_actions_t acts;
315301
res = posix_spawn_file_actions_init(&acts);
316-
if (res != 0) return kInvalidFd;
302+
if (res != 0)
303+
return false;
317304

318305
auto acts_cleanup = at_scope_exit([&] {
319306
posix_spawn_file_actions_destroy(&acts);
320307
});
321308

322-
res = posix_spawn_file_actions_adddup2(&acts, secondary_fd, STDIN_FILENO) ||
323-
posix_spawn_file_actions_adddup2(&acts, secondary_fd, STDOUT_FILENO) ||
324-
posix_spawn_file_actions_addclose(&acts, secondary_fd);
325-
if (res != 0) return kInvalidFd;
309+
res = posix_spawn_file_actions_adddup2(&acts, fd_stdin, STDIN_FILENO) ||
310+
posix_spawn_file_actions_adddup2(&acts, fd_stdout, STDOUT_FILENO) ||
311+
posix_spawn_file_actions_addclose(&acts, fd_stdin) ||
312+
posix_spawn_file_actions_addclose(&acts, fd_stdout);
313+
if (res != 0)
314+
return false;
326315

327316
// Spawn attributes
328317
posix_spawnattr_t attrs;
329318
res = posix_spawnattr_init(&attrs);
330-
if (res != 0) return kInvalidFd;
319+
if (res != 0)
320+
return false;
331321

332322
auto attrs_cleanup = at_scope_exit([&] {
333323
posix_spawnattr_destroy(&attrs);
@@ -336,50 +326,17 @@ static fd_t internal_spawn_impl(const char *argv[], const char *envp[],
336326
// In the spawned process, close all file descriptors that are not explicitly
337327
// described by the file actions object. This is Darwin-specific extension.
338328
res = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_CLOEXEC_DEFAULT);
339-
if (res != 0) return kInvalidFd;
329+
if (res != 0)
330+
return false;
340331

341332
// posix_spawn
342333
char **argv_casted = const_cast<char **>(argv);
343334
char **envp_casted = const_cast<char **>(envp);
344335
res = posix_spawn(pid, argv[0], &acts, &attrs, argv_casted, envp_casted);
345-
if (res != 0) return kInvalidFd;
346-
347-
// Disable echo in the new terminal, disable CR.
348-
struct termios termflags;
349-
tcgetattr(primary_fd, &termflags);
350-
termflags.c_oflag &= ~ONLCR;
351-
termflags.c_lflag &= ~ECHO;
352-
tcsetattr(primary_fd, TCSANOW, &termflags);
353-
354-
// On success, do not close primary_fd on scope exit.
355-
fd_t fd = primary_fd;
356-
primary_fd = kInvalidFd;
357-
358-
return fd;
359-
}
360-
361-
fd_t internal_spawn(const char *argv[], const char *envp[], pid_t *pid) {
362-
// The client program may close its stdin and/or stdout and/or stderr thus
363-
// allowing open/posix_openpt to reuse file descriptors 0, 1 or 2. In this
364-
// case the communication is broken if either the parent or the child tries to
365-
// close or duplicate these descriptors. We temporarily reserve these
366-
// descriptors here to prevent this.
367-
fd_t low_fds[3];
368-
size_t count = 0;
369-
370-
for (; count < 3; count++) {
371-
low_fds[count] = posix_openpt(O_RDWR);
372-
if (low_fds[count] >= STDERR_FILENO)
373-
break;
374-
}
375-
376-
fd_t fd = internal_spawn_impl(argv, envp, pid);
377-
378-
for (; count > 0; count--) {
379-
internal_close(low_fds[count]);
380-
}
336+
if (res != 0)
337+
return false;
381338

382-
return fd;
339+
return true;
383340
}
384341

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

compiler-rt/lib/sanitizer_common/sanitizer_posix.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ uptr internal_ptrace(int request, int pid, void *addr, void *data);
6767
uptr internal_waitpid(int pid, int *status, int options);
6868

6969
int internal_fork();
70-
fd_t internal_spawn(const char *argv[], const char *envp[], pid_t *pid);
70+
bool internal_spawn(const char* argv[], const char* envp[], pid_t* pid,
71+
fd_t stdin, fd_t stdout);
7172

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

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_internal.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class SymbolizerProcess {
8383
const char *SendCommand(const char *command);
8484

8585
protected:
86-
~SymbolizerProcess() {}
86+
~SymbolizerProcess();
8787

8888
/// The maximum number of arguments required to invoke a tool process.
8989
static const unsigned kArgVMax = 16;
@@ -114,6 +114,10 @@ class SymbolizerProcess {
114114
fd_t input_fd_;
115115
fd_t output_fd_;
116116

117+
// We hold on to the child's stdin fd (the read end of the pipe)
118+
// so that when we write to it, we don't get a SIGPIPE
119+
fd_t child_stdin_fd_;
120+
117121
InternalMmapVector<char> buffer_;
118122

119123
static const uptr kMaxTimesRestarted = 5;

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,10 +476,11 @@ const char *LLVMSymbolizer::FormatAndSendCommand(const char *command_prefix,
476476
return symbolizer_process_->SendCommand(buffer_);
477477
}
478478

479-
SymbolizerProcess::SymbolizerProcess(const char *path, bool use_posix_spawn)
479+
SymbolizerProcess::SymbolizerProcess(const char* path, bool use_posix_spawn)
480480
: path_(path),
481481
input_fd_(kInvalidFd),
482482
output_fd_(kInvalidFd),
483+
child_stdin_fd_(kInvalidFd),
483484
times_restarted_(0),
484485
failed_to_start_(false),
485486
reported_invalid_path_(false),
@@ -488,6 +489,11 @@ SymbolizerProcess::SymbolizerProcess(const char *path, bool use_posix_spawn)
488489
CHECK_NE(path_[0], '\0');
489490
}
490491

492+
SymbolizerProcess::~SymbolizerProcess() {
493+
if (child_stdin_fd_ != kInvalidFd)
494+
CloseFile(child_stdin_fd_);
495+
}
496+
491497
static bool IsSameModule(const char *path) {
492498
if (const char *ProcessName = GetProcessName()) {
493499
if (const char *SymbolizerName = StripModuleName(path)) {
@@ -533,6 +539,10 @@ bool SymbolizerProcess::Restart() {
533539
CloseFile(input_fd_);
534540
if (output_fd_ != kInvalidFd)
535541
CloseFile(output_fd_);
542+
if (child_stdin_fd_ != kInvalidFd) {
543+
CloseFile(child_stdin_fd_);
544+
child_stdin_fd_ = kInvalidFd; // Don't free in destructor
545+
}
536546
return StartSymbolizerSubprocess();
537547
}
538548

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -156,42 +156,45 @@ bool SymbolizerProcess::StartSymbolizerSubprocess() {
156156
Printf("\n");
157157
}
158158

159+
fd_t infd[2] = {}, outfd[2] = {};
160+
if (!CreateTwoHighNumberedPipes(infd, outfd)) {
161+
Report(
162+
"WARNING: Can't create a socket pair to start "
163+
"external symbolizer (errno: %d)\n",
164+
errno);
165+
return false;
166+
}
167+
159168
if (use_posix_spawn_) {
160169
# if SANITIZER_APPLE
161-
fd_t fd = internal_spawn(argv, const_cast<const char **>(GetEnvP()), &pid);
162-
if (fd == kInvalidFd) {
170+
bool success = internal_spawn(argv, const_cast<const char**>(GetEnvP()),
171+
&pid, outfd[0], infd[1]);
172+
if (!success) {
163173
Report("WARNING: failed to spawn external symbolizer (errno: %d)\n",
164174
errno);
175+
internal_close(infd[0]);
176+
internal_close(outfd[1]);
165177
return false;
166178
}
167-
168-
input_fd_ = fd;
169-
output_fd_ = fd;
170179
# else // SANITIZER_APPLE
171180
UNIMPLEMENTED();
172181
# endif // SANITIZER_APPLE
173182
} else {
174-
fd_t infd[2] = {}, outfd[2] = {};
175-
if (!CreateTwoHighNumberedPipes(infd, outfd)) {
176-
Report(
177-
"WARNING: Can't create a socket pair to start "
178-
"external symbolizer (errno: %d)\n",
179-
errno);
180-
return false;
181-
}
182-
183183
pid = StartSubprocess(path_, argv, GetEnvP(), /* stdin */ outfd[0],
184184
/* stdout */ infd[1]);
185185
if (pid < 0) {
186186
internal_close(infd[0]);
187187
internal_close(outfd[1]);
188188
return false;
189189
}
190-
191-
input_fd_ = infd[0];
192-
output_fd_ = outfd[1];
193190
}
194191

192+
input_fd_ = infd[0];
193+
output_fd_ = outfd[1];
194+
195+
// We intentionally hold on to the read-end so that we don't get a SIGPIPE
196+
child_stdin_fd_ = outfd[0];
197+
195198
CHECK_GT(pid, 0);
196199

197200
// Check that symbolizer subprocess started successfully.

0 commit comments

Comments
 (0)