Skip to content

Commit d949ff5

Browse files
committed
subprocess-posix: fewer ifdefs and fix ASan
1 parent 29fc730 commit d949ff5

File tree

2 files changed

+23
-25
lines changed

2 files changed

+23
-25
lines changed

osdep/compiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616
#define MP_FALLTHROUGH __attribute__((fallthrough))
1717
#define MP_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
1818
#define MP_UNUSED __attribute__((unused))
19+
#define MP_NO_ASAN __attribute__((no_sanitize("address")))
1920
#else
2021
#define MP_NORETURN
2122
#define MP_FALLTHROUGH do {} while (0)
2223
#define MP_WARN_UNUSED_RESULT
2324
#define MP_UNUSED
25+
#define MP_NO_ASAN
2426
#endif
2527

2628
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ < 202311L) && !defined(thread_local)

osdep/subprocess-posix.c

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
8079
static 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

9394
struct 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

102104
static 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

142144
child_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

151151
static 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

216216
done:
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().
227224
static 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

Comments
 (0)