@@ -79,6 +79,8 @@ class SubProcess {
79
79
static constexpr int kStdErrIdx = 1 ;
80
80
int parent_pipe_[2 ];
81
81
int child_pipe_[2 ];
82
+ // Internal pipe fds for notifying child wait completion.
83
+ int child_wait_pipe_fds_[2 ];
82
84
};
83
85
84
86
// Creates parent/child pipes for piping stdout/stderr from child to parent.
@@ -95,6 +97,13 @@ void SubProcess::CreatePipes() {
95
97
fcntl (parent_pipe_[channel], F_SETFL, O_NONBLOCK) != -1 ,
96
98
" Cannot make pipe non-blocking: " , strerror (errno));
97
99
}
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
+ }
98
107
}
99
108
100
109
void SubProcess::CloseChildPipes () {
@@ -193,25 +202,31 @@ void SubProcess::ReadChildOutput(std::string* stdout_output,
193
202
std::string* stderr_output) {
194
203
// Set up poll()-ing the pipes.
195
204
constexpr int fd_count = 2 ;
196
- struct pollfd pfd[fd_count];
205
+ struct pollfd pfd[fd_count + 1 ];
197
206
std::string* out_str[fd_count];
198
207
for (int channel : {kStdOutIdx , kStdErrIdx }) {
199
208
pfd[channel].fd = parent_pipe_[channel];
200
209
pfd[channel].events = POLLIN;
201
210
pfd[channel].revents = 0 ;
202
211
out_str[channel] = channel == kStdOutIdx ? stdout_output : stderr_output;
203
212
}
213
+ pfd[fd_count].fd = child_wait_pipe_fds_[0 ];
214
+ pfd[fd_count].events = POLLIN;
215
+ pfd[fd_count].revents = 0 ;
204
216
205
217
// Loop reading stdout and stderr from the child process.
206
218
int fd_remain = fd_count;
207
219
char buf[4096 ];
208
220
while (fd_remain > 0 ) {
209
- int ret = poll (pfd, fd_count, -1 );
221
+ int ret = poll (pfd, fd_count + 1 , -1 );
210
222
if ((ret == -1 ) && !ShouldRetry (errno)) {
211
223
FUZZTEST_INTERNAL_CHECK (false , " Cannot poll(): " , strerror (errno));
212
224
} else if (ret == 0 ) {
213
225
FUZZTEST_INTERNAL_CHECK (false , " Impossible timeout: " , strerror (errno));
214
226
} else if (ret > 0 ) {
227
+ if (pfd[fd_count].revents ) {
228
+ break ;
229
+ }
215
230
for (int channel : {kStdOutIdx , kStdErrIdx }) {
216
231
if ((pfd[channel].revents & (POLLIN | POLLHUP)) != 0 ) {
217
232
ssize_t n = read (pfd[channel].fd , buf, sizeof (buf));
@@ -247,7 +262,7 @@ int Wait(pid_t pid) {
247
262
// TODO(lszekeres): Consider optimizing this further by eliminating polling.
248
263
// Could potentially be done using pselect() to wait for SIGCHLD with a timeout.
249
264
// 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) {
251
266
int status;
252
267
const absl::Time wait_until = absl::Now () + timeout;
253
268
constexpr absl::Duration sleep_duration = absl::Milliseconds (100 );
@@ -259,17 +274,21 @@ int WaitWithTimeout(pid_t pid, absl::Duration timeout) {
259
274
if (absl::Now () > wait_until) {
260
275
FUZZTEST_INTERNAL_CHECK (kill (pid, SIGTERM) == 0 ,
261
276
" Cannot kill(): " , strerror (errno));
262
- return Wait (pid);
277
+ status = Wait (pid);
278
+ break ;
263
279
} else {
264
280
absl::SleepFor (sleep_duration);
265
281
continue ;
266
282
}
267
283
} else if (ret == pid && (WIFEXITED (status) || WIFSIGNALED (status))) {
268
- return status ;
284
+ break ;
269
285
} else {
270
286
FUZZTEST_INTERNAL_CHECK (false , " wait() error: " , strerror (errno));
271
287
}
272
288
}
289
+ FUZZTEST_INTERNAL_CHECK (close (fd_to_close) != -1 ,
290
+ " Cannot close fd_to_close: " , strerror (errno));
291
+ return status;
273
292
}
274
293
275
294
} // anonymous namespace
@@ -282,10 +301,13 @@ RunResults SubProcess::Run(
282
301
pid_t child_pid = StartChild (command_line, environment);
283
302
CloseChildPipes ();
284
303
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);
286
306
std::string stdout_output, stderr_output;
287
307
ReadChildOutput (&stdout_output, &stderr_output);
288
308
CloseParentPipes ();
309
+ FUZZTEST_INTERNAL_CHECK (close (child_wait_pipe_fds_[0 ]) != -1 ,
310
+ " Cannot close child wait pipe: " , strerror (errno));
289
311
return {TerminationStatus (status.get ()), stdout_output, stderr_output};
290
312
}
291
313
0 commit comments