@@ -122,23 +122,40 @@ int SpawnProcess(int& pid, FdToArgsFn&& fd_to_args)
122122 throw std::system_error (errno, std::system_category (), " socketpair" );
123123 }
124124
125+ // Evaluate the callback and build the argv array before forking.
126+ //
127+ // The parent process may be multi-threaded and holding internal library
128+ // locks at fork time. In that case, running code that allocates memory or
129+ // takes locks in the child between fork() and exec() can deadlock
130+ // indefinitely. Precomputing arguments in the parent avoids this.
131+ const std::vector<std::string> args{fd_to_args (fds[0 ])};
132+ const std::vector<char *> argv{MakeArgv (args)};
133+
125134 pid = fork ();
126135 if (pid == -1 ) {
127136 throw std::system_error (errno, std::system_category (), " fork" );
128137 }
129- // Parent process closes the descriptor for socket 0, child closes the descriptor for socket 1.
138+ // Parent process closes the descriptor for socket 0, child closes the
139+ // descriptor for socket 1. On failure, the parent throws, but the child
140+ // must _exit(1) (post-fork child must not throw).
130141 if (close (fds[pid ? 0 : 1 ]) != 0 ) {
131- throw std::system_error (errno, std::system_category (), " close" );
142+ if (pid) throw std::system_error (errno, std::system_category (), " close" );
143+ _exit (1 );
132144 }
145+
133146 if (!pid) {
134- // Child process must close all potentially open descriptors, except socket 0.
147+ // Child process must close all potentially open descriptors, except
148+ // socket 0. Do not throw, allocate, or do non-fork-safe work here.
135149 const int maxFd = MaxFd ();
136150 for (int fd = 3 ; fd < maxFd; ++fd) {
137151 if (fd != fds[0 ]) {
138152 close (fd);
139153 }
140154 }
141- ExecProcess (fd_to_args (fds[0 ]));
155+
156+ execvp (argv[0 ], argv.data ());
157+ perror (" execvp failed" );
158+ _exit (1 );
142159 }
143160 return fds[1 ];
144161}
@@ -158,7 +175,7 @@ void ExecProcess(const std::vector<std::string>& args)
158175int WaitProcess (int pid)
159176{
160177 int status;
161- if (::waitpid (pid, &status, 0 /* options */ ) != pid) {
178+ if (::waitpid (pid, &status, /* options= */ 0 ) != pid) {
162179 throw std::system_error (errno, std::system_category (), " waitpid" );
163180 }
164181 return status;
0 commit comments