1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <spawn.h>
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <gtest/gtest.h>
22
23 #include "SignalUtils.h"
24 #include "utils.h"
25
26 #include <android-base/file.h>
27 #include <android-base/strings.h>
28
29 // Old versions of glibc didn't have POSIX_SPAWN_SETSID.
30 #if __GLIBC__
31 # if !defined(POSIX_SPAWN_SETSID)
32 # define POSIX_SPAWN_SETSID 0
33 # endif
34 #else
35 #include <platform/bionic/reserved_signals.h>
36 #endif
37
TEST(spawn,posix_spawnattr_init_posix_spawnattr_destroy)38 TEST(spawn, posix_spawnattr_init_posix_spawnattr_destroy) {
39 posix_spawnattr_t sa;
40 ASSERT_EQ(0, posix_spawnattr_init(&sa));
41 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
42 }
43
TEST(spawn,posix_spawnattr_setflags_EINVAL)44 TEST(spawn, posix_spawnattr_setflags_EINVAL) {
45 posix_spawnattr_t sa;
46 ASSERT_EQ(0, posix_spawnattr_init(&sa));
47 ASSERT_EQ(EINVAL, posix_spawnattr_setflags(&sa, ~0));
48 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
49 }
50
TEST(spawn,posix_spawnattr_setflags_posix_spawnattr_getflags)51 TEST(spawn, posix_spawnattr_setflags_posix_spawnattr_getflags) {
52 posix_spawnattr_t sa;
53 ASSERT_EQ(0, posix_spawnattr_init(&sa));
54
55 ASSERT_EQ(0, posix_spawnattr_setflags(&sa, POSIX_SPAWN_RESETIDS));
56 short flags;
57 ASSERT_EQ(0, posix_spawnattr_getflags(&sa, &flags));
58 ASSERT_EQ(POSIX_SPAWN_RESETIDS, flags);
59
60 constexpr short all_flags = POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF |
61 POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSCHEDPARAM |
62 POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_USEVFORK | POSIX_SPAWN_SETSID;
63 ASSERT_EQ(0, posix_spawnattr_setflags(&sa, all_flags));
64 ASSERT_EQ(0, posix_spawnattr_getflags(&sa, &flags));
65 ASSERT_EQ(all_flags, flags);
66
67 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
68 }
69
TEST(spawn,posix_spawnattr_setpgroup_posix_spawnattr_getpgroup)70 TEST(spawn, posix_spawnattr_setpgroup_posix_spawnattr_getpgroup) {
71 posix_spawnattr_t sa;
72 ASSERT_EQ(0, posix_spawnattr_init(&sa));
73
74 ASSERT_EQ(0, posix_spawnattr_setpgroup(&sa, 123));
75 pid_t g;
76 ASSERT_EQ(0, posix_spawnattr_getpgroup(&sa, &g));
77 ASSERT_EQ(123, g);
78
79 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
80 }
81
TEST(spawn,posix_spawnattr_setsigmask_posix_spawnattr_getsigmask)82 TEST(spawn, posix_spawnattr_setsigmask_posix_spawnattr_getsigmask) {
83 posix_spawnattr_t sa;
84 ASSERT_EQ(0, posix_spawnattr_init(&sa));
85
86 sigset_t sigs;
87 ASSERT_EQ(0, posix_spawnattr_getsigmask(&sa, &sigs));
88 ASSERT_FALSE(sigismember(&sigs, SIGALRM));
89
90 sigset_t just_SIGALRM;
91 sigemptyset(&just_SIGALRM);
92 sigaddset(&just_SIGALRM, SIGALRM);
93 ASSERT_EQ(0, posix_spawnattr_setsigmask(&sa, &just_SIGALRM));
94
95 ASSERT_EQ(0, posix_spawnattr_getsigmask(&sa, &sigs));
96 ASSERT_TRUE(sigismember(&sigs, SIGALRM));
97
98 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
99 }
100
TEST(spawn,posix_spawnattr_setsigmask64_posix_spawnattr_getsigmask64)101 TEST(spawn, posix_spawnattr_setsigmask64_posix_spawnattr_getsigmask64) {
102 posix_spawnattr_t sa;
103 ASSERT_EQ(0, posix_spawnattr_init(&sa));
104
105 sigset64_t sigs;
106 ASSERT_EQ(0, posix_spawnattr_getsigmask64(&sa, &sigs));
107 ASSERT_FALSE(sigismember64(&sigs, SIGRTMIN));
108
109 sigset64_t just_SIGRTMIN;
110 sigemptyset64(&just_SIGRTMIN);
111 sigaddset64(&just_SIGRTMIN, SIGRTMIN);
112 ASSERT_EQ(0, posix_spawnattr_setsigmask64(&sa, &just_SIGRTMIN));
113
114 ASSERT_EQ(0, posix_spawnattr_getsigmask64(&sa, &sigs));
115 ASSERT_TRUE(sigismember64(&sigs, SIGRTMIN));
116
117 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
118 }
119
TEST(spawn,posix_spawnattr_setsigdefault_posix_spawnattr_getsigdefault)120 TEST(spawn, posix_spawnattr_setsigdefault_posix_spawnattr_getsigdefault) {
121 posix_spawnattr_t sa;
122 ASSERT_EQ(0, posix_spawnattr_init(&sa));
123
124 sigset_t sigs;
125 ASSERT_EQ(0, posix_spawnattr_getsigdefault(&sa, &sigs));
126 ASSERT_FALSE(sigismember(&sigs, SIGALRM));
127
128 sigset_t just_SIGALRM;
129 sigemptyset(&just_SIGALRM);
130 sigaddset(&just_SIGALRM, SIGALRM);
131 ASSERT_EQ(0, posix_spawnattr_setsigdefault(&sa, &just_SIGALRM));
132
133 ASSERT_EQ(0, posix_spawnattr_getsigdefault(&sa, &sigs));
134 ASSERT_TRUE(sigismember(&sigs, SIGALRM));
135
136 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
137 }
138
TEST(spawn,posix_spawnattr_setsigdefault64_posix_spawnattr_getsigdefault64)139 TEST(spawn, posix_spawnattr_setsigdefault64_posix_spawnattr_getsigdefault64) {
140 posix_spawnattr_t sa;
141 ASSERT_EQ(0, posix_spawnattr_init(&sa));
142
143 sigset64_t sigs;
144 ASSERT_EQ(0, posix_spawnattr_getsigdefault64(&sa, &sigs));
145 ASSERT_FALSE(sigismember64(&sigs, SIGRTMIN));
146
147 sigset64_t just_SIGRTMIN;
148 sigemptyset64(&just_SIGRTMIN);
149 sigaddset64(&just_SIGRTMIN, SIGRTMIN);
150 ASSERT_EQ(0, posix_spawnattr_setsigdefault64(&sa, &just_SIGRTMIN));
151
152 ASSERT_EQ(0, posix_spawnattr_getsigdefault64(&sa, &sigs));
153 ASSERT_TRUE(sigismember64(&sigs, SIGRTMIN));
154
155 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
156 }
157
TEST(spawn,posix_spawnattr_setsschedparam_posix_spawnattr_getsschedparam)158 TEST(spawn, posix_spawnattr_setsschedparam_posix_spawnattr_getsschedparam) {
159 posix_spawnattr_t sa;
160 ASSERT_EQ(0, posix_spawnattr_init(&sa));
161
162 sched_param sp;
163 ASSERT_EQ(0, posix_spawnattr_getschedparam(&sa, &sp));
164 ASSERT_EQ(0, sp.sched_priority);
165
166 sched_param sp123 = { .sched_priority = 123 };
167 ASSERT_EQ(0, posix_spawnattr_setschedparam(&sa, &sp123));
168
169 ASSERT_EQ(0, posix_spawnattr_getschedparam(&sa, &sp));
170 ASSERT_EQ(123, sp.sched_priority);
171
172 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
173 }
174
TEST(spawn,posix_spawnattr_setschedpolicy_posix_spawnattr_getschedpolicy)175 TEST(spawn, posix_spawnattr_setschedpolicy_posix_spawnattr_getschedpolicy) {
176 posix_spawnattr_t sa;
177 ASSERT_EQ(0, posix_spawnattr_init(&sa));
178
179 int p;
180 ASSERT_EQ(0, posix_spawnattr_getschedpolicy(&sa, &p));
181 ASSERT_EQ(0, p);
182
183 ASSERT_EQ(0, posix_spawnattr_setschedpolicy(&sa, SCHED_FIFO));
184
185 ASSERT_EQ(0, posix_spawnattr_getschedpolicy(&sa, &p));
186 ASSERT_EQ(SCHED_FIFO, p);
187
188 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
189 }
190
TEST(spawn,posix_spawn)191 TEST(spawn, posix_spawn) {
192 ExecTestHelper eth;
193 eth.SetArgs({BIN_DIR "true", nullptr});
194 pid_t pid;
195 ASSERT_EQ(0, posix_spawn(&pid, eth.GetArg0(), nullptr, nullptr, eth.GetArgs(), nullptr));
196 AssertChildExited(pid, 0);
197 }
198
TEST(spawn,posix_spawn_not_found)199 TEST(spawn, posix_spawn_not_found) {
200 ExecTestHelper eth;
201 eth.SetArgs({"true", nullptr});
202 pid_t pid;
203 ASSERT_EQ(0, posix_spawn(&pid, eth.GetArg0(), nullptr, nullptr, eth.GetArgs(), nullptr));
204 AssertChildExited(pid, 127);
205 }
206
TEST(spawn,posix_spawnp)207 TEST(spawn, posix_spawnp) {
208 ExecTestHelper eth;
209 eth.SetArgs({"true", nullptr});
210 pid_t pid;
211 ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), nullptr, nullptr, eth.GetArgs(), nullptr));
212 AssertChildExited(pid, 0);
213 }
214
TEST(spawn,posix_spawnp_not_found)215 TEST(spawn, posix_spawnp_not_found) {
216 ExecTestHelper eth;
217 eth.SetArgs({"does-not-exist", nullptr});
218 pid_t pid;
219 ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), nullptr, nullptr, eth.GetArgs(), nullptr));
220 AssertChildExited(pid, 127);
221 }
222
TEST(spawn,posix_spawn_environment)223 TEST(spawn, posix_spawn_environment) {
224 ExecTestHelper eth;
225 eth.SetArgs({"sh", "-c", "exit $posix_spawn_environment_test", nullptr});
226 eth.SetEnv({"posix_spawn_environment_test=66", nullptr});
227 pid_t pid;
228 ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), nullptr, nullptr, eth.GetArgs(), eth.GetEnv()));
229 AssertChildExited(pid, 66);
230 }
231
TEST(spawn,posix_spawn_file_actions)232 TEST(spawn, posix_spawn_file_actions) {
233 int fds[2];
234 ASSERT_NE(-1, pipe(fds));
235
236 posix_spawn_file_actions_t fa;
237 ASSERT_EQ(0, posix_spawn_file_actions_init(&fa));
238
239 ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[0]));
240 ASSERT_EQ(0, posix_spawn_file_actions_adddup2(&fa, fds[1], 1));
241 ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[1]));
242 // Check that close(2) failures are ignored by closing the same fd again.
243 ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[1]));
244 ASSERT_EQ(0, posix_spawn_file_actions_addopen(&fa, 56, "/proc/version", O_RDONLY, 0));
245
246 ExecTestHelper eth;
247 eth.SetArgs({"ls", "-l", "/proc/self/fd", nullptr});
248 pid_t pid;
249 ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), &fa, nullptr, eth.GetArgs(), eth.GetEnv()));
250 ASSERT_EQ(0, posix_spawn_file_actions_destroy(&fa));
251
252 ASSERT_EQ(0, close(fds[1]));
253 std::string content;
254 ASSERT_TRUE(android::base::ReadFdToString(fds[0], &content));
255 ASSERT_EQ(0, close(fds[0]));
256
257 AssertChildExited(pid, 0);
258
259 // We'll know the dup2 worked if we see any ls(1) output in our pipe.
260 // The open we can check manually...
261 bool open_to_fd_56_worked = false;
262 for (const auto& line : android::base::Split(content, "\n")) {
263 if (line.find(" 56 -> /proc/version") != std::string::npos) open_to_fd_56_worked = true;
264 }
265 ASSERT_TRUE(open_to_fd_56_worked);
266 }
267
CatFileToString(posix_spawnattr_t * sa,const char * path,std::string * content)268 static void CatFileToString(posix_spawnattr_t* sa, const char* path, std::string* content) {
269 int fds[2];
270 ASSERT_NE(-1, pipe(fds));
271
272 posix_spawn_file_actions_t fa;
273 ASSERT_EQ(0, posix_spawn_file_actions_init(&fa));
274 ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[0]));
275 ASSERT_EQ(0, posix_spawn_file_actions_adddup2(&fa, fds[1], 1));
276 ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, fds[1]));
277
278 ExecTestHelper eth;
279 eth.SetArgs({"cat", path, nullptr});
280 pid_t pid;
281 ASSERT_EQ(0, posix_spawnp(&pid, eth.GetArg0(), &fa, sa, eth.GetArgs(), nullptr));
282 ASSERT_EQ(0, posix_spawn_file_actions_destroy(&fa));
283
284 ASSERT_EQ(0, close(fds[1]));
285 ASSERT_TRUE(android::base::ReadFdToString(fds[0], content));
286 ASSERT_EQ(0, close(fds[0]));
287 AssertChildExited(pid, 0);
288 }
289
290 struct ProcStat {
291 pid_t pid;
292 pid_t ppid;
293 pid_t pgrp;
294 pid_t sid;
295 };
296
GetChildStat(posix_spawnattr_t * sa,ProcStat * ps)297 static __attribute__((unused)) void GetChildStat(posix_spawnattr_t* sa, ProcStat* ps) {
298 std::string content;
299 CatFileToString(sa, "/proc/self/stat", &content);
300
301 ASSERT_EQ(4, sscanf(content.c_str(), "%d (cat) %*c %d %d %d", &ps->pid, &ps->ppid, &ps->pgrp,
302 &ps->sid));
303
304 ASSERT_EQ(getpid(), ps->ppid);
305 }
306
307 struct ProcStatus {
308 uint64_t sigblk;
309 uint64_t sigign;
310 };
311
GetChildStatus(posix_spawnattr_t * sa,ProcStatus * ps)312 static void __attribute__((unused)) GetChildStatus(posix_spawnattr_t* sa, ProcStatus* ps) {
313 std::string content;
314 CatFileToString(sa, "/proc/self/status", &content);
315
316 bool saw_blk = false;
317 bool saw_ign = false;
318 for (const auto& line : android::base::Split(content, "\n")) {
319 if (sscanf(line.c_str(), "SigBlk: %" SCNx64, &ps->sigblk) == 1) saw_blk = true;
320 if (sscanf(line.c_str(), "SigIgn: %" SCNx64, &ps->sigign) == 1) saw_ign = true;
321 }
322 ASSERT_TRUE(saw_blk);
323 ASSERT_TRUE(saw_ign);
324 }
325
TEST(spawn,posix_spawn_POSIX_SPAWN_SETSID_clear)326 TEST(spawn, posix_spawn_POSIX_SPAWN_SETSID_clear) {
327 pid_t parent_sid = getsid(0);
328
329 posix_spawnattr_t sa;
330 ASSERT_EQ(0, posix_spawnattr_init(&sa));
331 ASSERT_EQ(0, posix_spawnattr_setflags(&sa, 0));
332
333 ProcStat ps = {};
334 GetChildStat(&sa, &ps);
335 ASSERT_EQ(parent_sid, ps.sid);
336 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
337 }
338
TEST(spawn,posix_spawn_POSIX_SPAWN_SETSID_set)339 TEST(spawn, posix_spawn_POSIX_SPAWN_SETSID_set) {
340 pid_t parent_sid = getsid(0);
341
342 posix_spawnattr_t sa;
343 ASSERT_EQ(0, posix_spawnattr_init(&sa));
344 ASSERT_EQ(0, posix_spawnattr_setflags(&sa, POSIX_SPAWN_SETSID));
345
346 ProcStat ps = {};
347 GetChildStat(&sa, &ps);
348 ASSERT_NE(parent_sid, ps.sid);
349 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
350 }
351
TEST(spawn,posix_spawn_POSIX_SPAWN_SETPGROUP_clear)352 TEST(spawn, posix_spawn_POSIX_SPAWN_SETPGROUP_clear) {
353 pid_t parent_pgrp = getpgrp();
354
355 posix_spawnattr_t sa;
356 ASSERT_EQ(0, posix_spawnattr_init(&sa));
357 ASSERT_EQ(0, posix_spawnattr_setflags(&sa, 0));
358
359 ProcStat ps = {};
360 GetChildStat(&sa, &ps);
361 ASSERT_EQ(parent_pgrp, ps.pgrp);
362 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
363 }
364
TEST(spawn,posix_spawn_POSIX_SPAWN_SETPGROUP_set)365 TEST(spawn, posix_spawn_POSIX_SPAWN_SETPGROUP_set) {
366 pid_t parent_pgrp = getpgrp();
367
368 posix_spawnattr_t sa;
369 ASSERT_EQ(0, posix_spawnattr_init(&sa));
370 ASSERT_EQ(0, posix_spawnattr_setpgroup(&sa, 0));
371 ASSERT_EQ(0, posix_spawnattr_setflags(&sa, POSIX_SPAWN_SETPGROUP));
372
373 ProcStat ps = {};
374 GetChildStat(&sa, &ps);
375 ASSERT_NE(parent_pgrp, ps.pgrp);
376 // Setting pgid 0 means "the same as the caller's pid".
377 ASSERT_EQ(ps.pid, ps.pgrp);
378 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
379 }
380
TEST(spawn,posix_spawn_POSIX_SPAWN_SETSIGMASK)381 TEST(spawn, posix_spawn_POSIX_SPAWN_SETSIGMASK) {
382 #if defined(__GLIBC__)
383 GTEST_SKIP() << "glibc doesn't ignore the same signals.";
384 #else
385 // Block SIGBUS in the parent...
386 sigset_t just_SIGBUS;
387 sigemptyset(&just_SIGBUS);
388 sigaddset(&just_SIGBUS, SIGBUS);
389 ASSERT_EQ(0, sigprocmask(SIG_BLOCK, &just_SIGBUS, nullptr));
390
391 posix_spawnattr_t sa;
392 ASSERT_EQ(0, posix_spawnattr_init(&sa));
393
394 // Ask for only SIGALRM to be blocked in the child...
395 sigset_t just_SIGALRM;
396 sigemptyset(&just_SIGALRM);
397 sigaddset(&just_SIGALRM, SIGALRM);
398 ASSERT_EQ(0, posix_spawnattr_setsigmask(&sa, &just_SIGALRM));
399 ASSERT_EQ(0, posix_spawnattr_setflags(&sa, POSIX_SPAWN_SETSIGMASK));
400
401 // Check that's what happens...
402 ProcStatus ps = {};
403 GetChildStatus(&sa, &ps);
404
405 // TIMER_SIGNAL should also be blocked.
406 uint64_t expected_blocked = 0;
407 SignalSetAdd(&expected_blocked, SIGALRM);
408 SignalSetAdd(&expected_blocked, BIONIC_SIGNAL_POSIX_TIMERS);
409 EXPECT_EQ(expected_blocked, ps.sigblk);
410
411 uint64_t expected_ignored = 0;
412 SignalSetAdd(&expected_ignored, BIONIC_SIGNAL_ART_PROFILER);
413 EXPECT_EQ(expected_ignored, ps.sigign);
414
415 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
416 #endif
417 }
418
TEST(spawn,posix_spawn_POSIX_SPAWN_SETSIGDEF)419 TEST(spawn, posix_spawn_POSIX_SPAWN_SETSIGDEF) {
420 #if defined(__GLIBC__)
421 GTEST_SKIP() << "glibc doesn't ignore the same signals.";
422 #else
423 // Ignore SIGALRM and SIGCONT in the parent...
424 ASSERT_NE(SIG_ERR, signal(SIGALRM, SIG_IGN));
425 ASSERT_NE(SIG_ERR, signal(SIGCONT, SIG_IGN));
426
427 posix_spawnattr_t sa;
428 ASSERT_EQ(0, posix_spawnattr_init(&sa));
429
430 // Ask for SIGALRM to be defaulted in the child...
431 sigset_t just_SIGALRM;
432 sigemptyset(&just_SIGALRM);
433 sigaddset(&just_SIGALRM, SIGALRM);
434
435 ASSERT_EQ(0, posix_spawnattr_setsigdefault(&sa, &just_SIGALRM));
436 ASSERT_EQ(0, posix_spawnattr_setflags(&sa, POSIX_SPAWN_SETSIGDEF));
437
438 // Check that's what happens...
439 ProcStatus ps = {};
440 GetChildStatus(&sa, &ps);
441
442 // TIMER_SIGNAL should be blocked.
443 uint64_t expected_blocked = 0;
444 SignalSetAdd(&expected_blocked, BIONIC_SIGNAL_POSIX_TIMERS);
445 EXPECT_EQ(expected_blocked, ps.sigblk);
446
447 uint64_t expected_ignored = 0;
448 SignalSetAdd(&expected_ignored, SIGCONT);
449 SignalSetAdd(&expected_ignored, BIONIC_SIGNAL_ART_PROFILER);
450 EXPECT_EQ(expected_ignored, ps.sigign);
451
452 ASSERT_EQ(0, posix_spawnattr_destroy(&sa));
453 #endif
454 }
455
TEST(spawn,signal_stress)456 TEST(spawn, signal_stress) {
457 // Ensure that posix_spawn doesn't restore the caller's signal mask in the
458 // child without first defaulting any caught signals (http://b/68707996).
459 static pid_t parent = getpid();
460
461 setpgid(0, 0);
462 signal(SIGRTMIN, SIG_IGN);
463
464 pid_t pid = fork();
465 ASSERT_NE(-1, pid);
466
467 if (pid == 0) {
468 for (size_t i = 0; i < 1024; ++i) {
469 kill(0, SIGRTMIN);
470 usleep(10);
471 }
472 _exit(99);
473 }
474
475 // We test both with and without attributes, because they used to be
476 // different codepaths. We also test with an empty `sigdefault` set.
477 posix_spawnattr_t attr1;
478 posix_spawnattr_init(&attr1);
479
480 sigset_t empty_mask = {};
481 posix_spawnattr_t attr2;
482 posix_spawnattr_init(&attr2);
483 posix_spawnattr_setflags(&attr2, POSIX_SPAWN_SETSIGDEF);
484 posix_spawnattr_setsigdefault(&attr2, &empty_mask);
485
486 posix_spawnattr_t* attrs[] = { nullptr, &attr1, &attr2 };
487
488 // We use a real-time signal because that's a tricky case for LP32
489 // because our sigset_t was too small.
490 ScopedSignalHandler ssh(SIGRTMIN, [](int) { ASSERT_EQ(getpid(), parent); });
491
492 const size_t pid_count = 128;
493 pid_t spawned_pids[pid_count];
494
495 ExecTestHelper eth;
496 eth.SetArgs({"true", nullptr});
497 for (size_t i = 0; i < pid_count; ++i) {
498 pid_t spawned_pid;
499 ASSERT_EQ(0, posix_spawn(&spawned_pid, "true", nullptr, attrs[i % 3], eth.GetArgs(), nullptr));
500 spawned_pids[i] = spawned_pid;
501 }
502
503 for (pid_t spawned_pid : spawned_pids) {
504 ASSERT_EQ(spawned_pid, TEMP_FAILURE_RETRY(waitpid(spawned_pid, nullptr, 0)));
505 }
506
507 AssertChildExited(pid, 99);
508 }
509