1 //===-- sanitizer_symbolizer_process_libcdep.cc ---------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Implementation of SymbolizerProcess used by external symbolizers.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "sanitizer_platform.h"
15 #if SANITIZER_POSIX
16 #include "sanitizer_posix.h"
17 #include "sanitizer_symbolizer_internal.h"
18
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23
24 #if SANITIZER_MAC
25 #include <util.h> // for forkpty()
26 #endif // SANITIZER_MAC
27
28 namespace __sanitizer {
29
SymbolizerProcess(const char * path,bool use_forkpty)30 SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty)
31 : path_(path),
32 input_fd_(kInvalidFd),
33 output_fd_(kInvalidFd),
34 times_restarted_(0),
35 failed_to_start_(false),
36 reported_invalid_path_(false),
37 use_forkpty_(use_forkpty) {
38 CHECK(path_);
39 CHECK_NE(path_[0], '\0');
40 }
41
SendCommand(const char * command)42 const char *SymbolizerProcess::SendCommand(const char *command) {
43 for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
44 // Start or restart symbolizer if we failed to send command to it.
45 if (const char *res = SendCommandImpl(command))
46 return res;
47 Restart();
48 }
49 if (!failed_to_start_) {
50 Report("WARNING: Failed to use and restart external symbolizer!\n");
51 failed_to_start_ = true;
52 }
53 return 0;
54 }
55
Restart()56 bool SymbolizerProcess::Restart() {
57 if (input_fd_ != kInvalidFd)
58 internal_close(input_fd_);
59 if (output_fd_ != kInvalidFd)
60 internal_close(output_fd_);
61 return StartSymbolizerSubprocess();
62 }
63
SendCommandImpl(const char * command)64 const char *SymbolizerProcess::SendCommandImpl(const char *command) {
65 if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
66 return 0;
67 if (!WriteToSymbolizer(command, internal_strlen(command)))
68 return 0;
69 if (!ReadFromSymbolizer(buffer_, kBufferSize))
70 return 0;
71 return buffer_;
72 }
73
ReadFromSymbolizer(char * buffer,uptr max_length)74 bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) {
75 if (max_length == 0)
76 return true;
77 uptr read_len = 0;
78 while (true) {
79 uptr just_read = internal_read(input_fd_, buffer + read_len,
80 max_length - read_len - 1);
81 // We can't read 0 bytes, as we don't expect external symbolizer to close
82 // its stdout.
83 if (just_read == 0 || just_read == (uptr)-1) {
84 Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
85 return false;
86 }
87 read_len += just_read;
88 if (ReachedEndOfOutput(buffer, read_len))
89 break;
90 }
91 buffer[read_len] = '\0';
92 return true;
93 }
94
WriteToSymbolizer(const char * buffer,uptr length)95 bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) {
96 if (length == 0)
97 return true;
98 uptr write_len = internal_write(output_fd_, buffer, length);
99 if (write_len == 0 || write_len == (uptr)-1) {
100 Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
101 return false;
102 }
103 return true;
104 }
105
StartSymbolizerSubprocess()106 bool SymbolizerProcess::StartSymbolizerSubprocess() {
107 if (!FileExists(path_)) {
108 if (!reported_invalid_path_) {
109 Report("WARNING: invalid path to external symbolizer!\n");
110 reported_invalid_path_ = true;
111 }
112 return false;
113 }
114
115 int pid;
116 if (use_forkpty_) {
117 #if SANITIZER_MAC
118 fd_t fd = kInvalidFd;
119 // Use forkpty to disable buffering in the new terminal.
120 pid = forkpty(&fd, 0, 0, 0);
121 if (pid == -1) {
122 // forkpty() failed.
123 Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
124 errno);
125 return false;
126 } else if (pid == 0) {
127 // Child subprocess.
128 ExecuteWithDefaultArgs(path_);
129 internal__exit(1);
130 }
131
132 // Continue execution in parent process.
133 input_fd_ = output_fd_ = fd;
134
135 // Disable echo in the new terminal, disable CR.
136 struct termios termflags;
137 tcgetattr(fd, &termflags);
138 termflags.c_oflag &= ~ONLCR;
139 termflags.c_lflag &= ~ECHO;
140 tcsetattr(fd, TCSANOW, &termflags);
141 #else // SANITIZER_MAC
142 UNIMPLEMENTED();
143 #endif // SANITIZER_MAC
144 } else {
145 int *infd = NULL;
146 int *outfd = NULL;
147 // The client program may close its stdin and/or stdout and/or stderr
148 // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
149 // In this case the communication between the forked processes may be
150 // broken if either the parent or the child tries to close or duplicate
151 // these descriptors. The loop below produces two pairs of file
152 // descriptors, each greater than 2 (stderr).
153 int sock_pair[5][2];
154 for (int i = 0; i < 5; i++) {
155 if (pipe(sock_pair[i]) == -1) {
156 for (int j = 0; j < i; j++) {
157 internal_close(sock_pair[j][0]);
158 internal_close(sock_pair[j][1]);
159 }
160 Report("WARNING: Can't create a socket pair to start "
161 "external symbolizer (errno: %d)\n", errno);
162 return false;
163 } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {
164 if (infd == NULL) {
165 infd = sock_pair[i];
166 } else {
167 outfd = sock_pair[i];
168 for (int j = 0; j < i; j++) {
169 if (sock_pair[j] == infd) continue;
170 internal_close(sock_pair[j][0]);
171 internal_close(sock_pair[j][1]);
172 }
173 break;
174 }
175 }
176 }
177 CHECK(infd);
178 CHECK(outfd);
179
180 // Real fork() may call user callbacks registered with pthread_atfork().
181 pid = internal_fork();
182 if (pid == -1) {
183 // Fork() failed.
184 internal_close(infd[0]);
185 internal_close(infd[1]);
186 internal_close(outfd[0]);
187 internal_close(outfd[1]);
188 Report("WARNING: failed to fork external symbolizer "
189 " (errno: %d)\n", errno);
190 return false;
191 } else if (pid == 0) {
192 // Child subprocess.
193 internal_close(STDOUT_FILENO);
194 internal_close(STDIN_FILENO);
195 internal_dup2(outfd[0], STDIN_FILENO);
196 internal_dup2(infd[1], STDOUT_FILENO);
197 internal_close(outfd[0]);
198 internal_close(outfd[1]);
199 internal_close(infd[0]);
200 internal_close(infd[1]);
201 for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--)
202 internal_close(fd);
203 ExecuteWithDefaultArgs(path_);
204 internal__exit(1);
205 }
206
207 // Continue execution in parent process.
208 internal_close(outfd[0]);
209 internal_close(infd[1]);
210 input_fd_ = infd[0];
211 output_fd_ = outfd[1];
212 }
213
214 // Check that symbolizer subprocess started successfully.
215 int pid_status;
216 SleepForMillis(kSymbolizerStartupTimeMillis);
217 int exited_pid = waitpid(pid, &pid_status, WNOHANG);
218 if (exited_pid != 0) {
219 // Either waitpid failed, or child has already exited.
220 Report("WARNING: external symbolizer didn't start up correctly!\n");
221 return false;
222 }
223
224 return true;
225 }
226
227 } // namespace __sanitizer
228
229 #endif // SANITIZER_POSIX
230