@@ -79,6 +79,8 @@ class SubProcess {
7979 static constexpr int kStdErrIdx = 1 ;
8080 int parent_pipe_[2 ];
8181 int child_pipe_[2 ];
82+ // Internal pipe fds for notifying child wait completion.
83+ int child_wait_pipe_fds_[2 ];
8284};
8385
8486// Creates parent/child pipes for piping stdout/stderr from child to parent.
@@ -95,6 +97,13 @@ void SubProcess::CreatePipes() {
9597 fcntl (parent_pipe_[channel], F_SETFL, O_NONBLOCK) != -1 ,
9698 " Cannot make pipe non-blocking: " , strerror (errno));
9799 }
100+ FUZZTEST_INTERNAL_CHECK (pipe (child_wait_pipe_fds_) == 0 ,
101+ " Cannot create child wait pipe: " , strerror (errno));
102+ for (int i = 0 ; i < 2 ; ++i) {
103+ FUZZTEST_INTERNAL_CHECK (
104+ fcntl (child_wait_pipe_fds_[i], F_SETFD, FD_CLOEXEC) != -1 ,
105+ " Cannot make child wait pipe fds close on exit: " , strerror (errno));
106+ }
98107}
99108
100109void SubProcess::CloseChildPipes () {
@@ -193,25 +202,31 @@ void SubProcess::ReadChildOutput(std::string* stdout_output,
193202 std::string* stderr_output) {
194203 // Set up poll()-ing the pipes.
195204 constexpr int fd_count = 2 ;
196- struct pollfd pfd[fd_count];
205+ struct pollfd pfd[fd_count + 1 ];
197206 std::string* out_str[fd_count];
198207 for (int channel : {kStdOutIdx , kStdErrIdx }) {
199208 pfd[channel].fd = parent_pipe_[channel];
200209 pfd[channel].events = POLLIN;
201210 pfd[channel].revents = 0 ;
202211 out_str[channel] = channel == kStdOutIdx ? stdout_output : stderr_output;
203212 }
213+ pfd[fd_count].fd = child_wait_pipe_fds_[0 ];
214+ pfd[fd_count].events = POLLIN;
215+ pfd[fd_count].revents = 0 ;
204216
205217 // Loop reading stdout and stderr from the child process.
206218 int fd_remain = fd_count;
207219 char buf[4096 ];
208220 while (fd_remain > 0 ) {
209- int ret = poll (pfd, fd_count, -1 );
221+ int ret = poll (pfd, fd_count + 1 , -1 );
210222 if ((ret == -1 ) && !ShouldRetry (errno)) {
211223 FUZZTEST_INTERNAL_CHECK (false , " Cannot poll(): " , strerror (errno));
212224 } else if (ret == 0 ) {
213225 FUZZTEST_INTERNAL_CHECK (false , " Impossible timeout: " , strerror (errno));
214226 } else if (ret > 0 ) {
227+ if (pfd[fd_count].revents ) {
228+ break ;
229+ }
215230 for (int channel : {kStdOutIdx , kStdErrIdx }) {
216231 if ((pfd[channel].revents & (POLLIN | POLLHUP)) != 0 ) {
217232 ssize_t n = read (pfd[channel].fd , buf, sizeof (buf));
@@ -247,7 +262,7 @@ int Wait(pid_t pid) {
247262// TODO(lszekeres): Consider optimizing this further by eliminating polling.
248263// Could potentially be done using pselect() to wait for SIGCHLD with a timeout.
249264// I.e., by setting all args to null, except timeout with a sigmask for SIGCHLD.
250- int WaitWithTimeout (pid_t pid, absl::Duration timeout) {
265+ int WaitWithTimeout (pid_t pid, int fd_to_close, absl::Duration timeout) {
251266 int status;
252267 const absl::Time wait_until = absl::Now () + timeout;
253268 constexpr absl::Duration sleep_duration = absl::Milliseconds (100 );
@@ -259,17 +274,21 @@ int WaitWithTimeout(pid_t pid, absl::Duration timeout) {
259274 if (absl::Now () > wait_until) {
260275 FUZZTEST_INTERNAL_CHECK (kill (pid, SIGTERM) == 0 ,
261276 " Cannot kill(): " , strerror (errno));
262- return Wait (pid);
277+ status = Wait (pid);
278+ break ;
263279 } else {
264280 absl::SleepFor (sleep_duration);
265281 continue ;
266282 }
267283 } else if (ret == pid && (WIFEXITED (status) || WIFSIGNALED (status))) {
268- return status ;
284+ break ;
269285 } else {
270286 FUZZTEST_INTERNAL_CHECK (false , " wait() error: " , strerror (errno));
271287 }
272288 }
289+ FUZZTEST_INTERNAL_CHECK (close (fd_to_close) != -1 ,
290+ " Cannot close fd_to_close: " , strerror (errno));
291+ return status;
273292}
274293
275294} // anonymous namespace
@@ -282,10 +301,13 @@ RunResults SubProcess::Run(
282301 pid_t child_pid = StartChild (command_line, environment);
283302 CloseChildPipes ();
284303 std::future<int > status =
285- std::async (std::launch::async, &WaitWithTimeout, child_pid, timeout);
304+ std::async (std::launch::async, &WaitWithTimeout, child_pid,
305+ child_wait_pipe_fds_[1 ], timeout);
286306 std::string stdout_output, stderr_output;
287307 ReadChildOutput (&stdout_output, &stderr_output);
288308 CloseParentPipes ();
309+ FUZZTEST_INTERNAL_CHECK (close (child_wait_pipe_fds_[0 ]) != -1 ,
310+ " Cannot close child wait pipe: " , strerror (errno));
289311 return {TerminationStatus (status.get ()), stdout_output, stderr_output};
290312}
291313
0 commit comments