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