@@ -74,11 +74,12 @@ static int as_execvpe(const char *path, const char *file, char *const argv[],
7474 return -1 ;
7575}
7676
77- #if !HAVE_RFORK
7877// In the child process, resets the signal mask to defaults. Also clears any
7978// signal handlers first so nothing funny happens.
8079static void reset_signals_child (void )
8180{
81+ #if !HAVE_RFORK
82+ // RFSPAWN has reset all signal actions in the child to default already
8283 struct sigaction sa = { 0 };
8384 sigset_t sigmask ;
8485 sa .sa_handler = SIG_DFL ;
@@ -87,29 +88,33 @@ static void reset_signals_child(void)
8788 for (int nr = 1 ; nr <= SIGNAL_MAX ; nr ++ )
8889 sigaction (nr , & sa , NULL );
8990 sigprocmask (SIG_SETMASK , & sigmask , NULL );
90- }
9191#endif
92+ }
9293
9394struct child_args {
9495 const char * path ;
9596 struct mp_subprocess_opts * opts ;
9697 int * src_fds ;
9798 void * child_stack ;
9899 int pipe_end ;
100+ bool failed ;
99101 bool detach ;
100102};
101103
102104static pid_t spawn_process_inner (const char * path , struct mp_subprocess_opts * opts ,
103105 int src_fds [], bool detach , void * stacks []);
104106
105- static int child_main (void * args )
107+ // This function is called from a clone(CLONE_VM)/rfork_thread context where
108+ // the child shares the parent's address space. Use MP_NO_ASAN to avoid false
109+ // positives from ASan when the child writes to shared memory.
110+ MP_NO_ASAN static int child_main (void * args )
106111{
107112 struct child_args * child_args = args ;
108113 const char * path = child_args -> path ;
109114 struct mp_subprocess_opts * opts = child_args -> opts ;
110115 int * src_fds = child_args -> src_fds ;
111116 void * child_stack = child_args -> child_stack ;
112- int * pipe_end = & child_args -> pipe_end ;
117+ int pipe_end = child_args -> pipe_end ;
113118 bool detach = child_args -> detach ;
114119
115120 if (detach ) {
@@ -119,10 +124,7 @@ static int child_main(void* args)
119124 return 0 ;
120125 }
121126
122- // RFSPAWN has reset all signal actions in the child to default
123- #if !HAVE_RFORK
124127 reset_signals_child ();
125- #endif
126128
127129 for (int n = 0 ; n < opts -> num_fds ; n ++ ) {
128130 if (src_fds [n ] == opts -> fds [n ].fd ) {
@@ -140,35 +142,35 @@ static int child_main(void* args)
140142 as_execvpe (path , opts -> exe , opts -> args , opts -> env ? opts -> env : environ );
141143
142144child_failed :
143- #if HAVE_CLONE || HAVE_RFORK
144- * pipe_end = 1 ;
145- #else
146- (void )write (* pipe_end , & (char ){1 }, 1 ); // shouldn't be able to fail
147- #endif
145+ child_args -> failed = true;
146+ if (pipe_end >= 0 )
147+ (void )write (pipe_end , & (char ){1 }, 1 ); // shouldn't be able to fail
148148 return 1 ;
149149}
150150
151151static pid_t spawn_process_inner (const char * path , struct mp_subprocess_opts * opts ,
152152 int src_fds [], bool detach , void * stacks [])
153153{
154154 pid_t fres = 0 ;
155- int r ;
155+ int r = 0 ;
156156
157157 struct child_args child_args = {
158158 .path = path ,
159159 .opts = opts ,
160160 .src_fds = src_fds ,
161161 .child_stack = stacks [1 ],
162- .pipe_end = 0 ,
162+ .pipe_end = -1 ,
163+ .failed = false,
163164 .detach = detach ,
164165 };
165166
167+ int p [2 ] = {-1 , -1 };
168+
166169#if HAVE_RFORK
167170 fres = rfork_thread (RFSPAWN , stacks [0 ], child_main , & child_args );
168171#elif HAVE_CLONE
169172 fres = clone (child_main , stacks [0 ], CLONE_VM | CLONE_VFORK | SIGCHLD , & child_args );
170173#else
171- int p [2 ] = {-1 , -1 };
172174 // We setup a communication pipe to signal failure. Since the child calls
173175 // exec() and becomes the calling process, we don't know if or when the
174176 // child process successfully ran exec() just from the PID.
@@ -191,9 +193,7 @@ static pid_t spawn_process_inner(const char *path, struct mp_subprocess_opts *op
191193 goto done ;
192194 }
193195
194- #if HAVE_CLONE || HAVE_RFORK
195- r = child_args .pipe_end ;
196- #else
196+ #if !HAVE_CLONE && !HAVE_RFORK
197197 if (fres == 0 ) {
198198 _exit (child_main (& child_args ));
199199 }
@@ -206,34 +206,32 @@ static pid_t spawn_process_inner(const char *path, struct mp_subprocess_opts *op
206206#endif
207207
208208 // If exec()ing child failed, collect it immediately.
209- if (detach || r != 0 ) {
209+ if (detach || child_args . failed || r != 0 ) {
210210 int child_status = 0 ;
211211 while (waitpid (fres , & child_status , 0 ) < 0 && errno == EINTR ) {}
212212 if (r != 0 || !WIFEXITED (child_status ) || WEXITSTATUS (child_status ) != 0 )
213213 fres = 0 ;
214214 }
215215
216216done :
217- #if !HAVE_CLONE && !HAVE_RFORK
218217 SAFE_CLOSE (p [0 ]);
219218 SAFE_CLOSE (p [1 ]);
220- #endif
221219
222220 return fres ;
223221}
224222
225223// Returns 0 on any error, valid PID on success.
226- // This function must be async-signal-safe, as it may be called from a fork().
227224static pid_t spawn_process (const char * path , struct mp_subprocess_opts * opts ,
228225 int src_fds [])
229226{
230227 bool detach = opts -> detach ;
231228 void * stacks [2 ];
229+ void * ctx = NULL ;
232230
233231#if HAVE_CLONE || HAVE_RFORK
234232 // pre-allocate stacks so we can be async-signal-safe in spawn_process_inner()
235233 const size_t stack_size = 0x8000 ;
236- void * ctx = talloc_new (NULL );
234+ ctx = talloc_new (NULL );
237235 // stack should be aligned to 16 bytes, which is guaranteed by malloc
238236 stacks [0 ] = (int8_t * )talloc_size (ctx , stack_size ) + stack_size ;
239237 if (detach ) stacks [1 ] = (int8_t * )talloc_size (ctx , stack_size ) + stack_size ;
@@ -252,9 +250,7 @@ static pid_t spawn_process(const char *path, struct mp_subprocess_opts *opts,
252250 pthread_sigmask (SIG_SETMASK , & oldmask , NULL );
253251#endif
254252
255- #if HAVE_CLONE || HAVE_RFORK
256253 talloc_free (ctx );
257- #endif
258254
259255 return fres ;
260256}
0 commit comments