1230479Snetchild// Copyright 2018 The Fuchsia Authors. All rights reserved.
2230479Snetchild// Use of this source code is governed by a BSD-style license that can be
3230479Snetchild// found in the LICENSE file.
4230479Snetchild
5230479Snetchild#include <unittest/unittest.h>
6230479Snetchild
7230479Snetchild#include <fcntl.h>
8230479Snetchild#include <lib/fdio/io.h>
9230479Snetchild#include <lib/fdio/spawn.h>
10230479Snetchild#include <lib/fdio/util.h>
11230479Snetchild#include <lib/zx/channel.h>
12230479Snetchild#include <lib/zx/job.h>
13230479Snetchild#include <lib/zx/process.h>
14230479Snetchild#include <lib/zx/socket.h>
15230479Snetchild#include <stdlib.h>
16230479Snetchild#include <unistd.h>
17230479Snetchild#include <zircon/limits.h>
18230479Snetchild#include <zircon/processargs.h>
19230479Snetchild#include <zircon/syscalls/policy.h>
20230479Snetchild
21230479Snetchildstatic constexpr char kSpawnChild[] = "/boot/bin/spawn-child";
22static constexpr char kSpawnLauncher[] = "/boot/bin/spawn-launcher";
23
24static bool has_fd(int fd) {
25    zx_handle_t handles[FDIO_MAX_HANDLES];
26    uint32_t ids[FDIO_MAX_HANDLES];
27    zx_status_t status = fdio_clone_fd(fd, fd + 50, handles, ids);
28    if (status > 0) {
29        size_t n = static_cast<size_t>(status);
30        zx_handle_close_many(handles, n);
31        return true;
32    }
33    return false;
34}
35
36static int64_t join(const zx::process& process) {
37    zx_status_t status = process.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), nullptr);
38    ASSERT_EQ(ZX_OK, status);
39    zx_info_process_t proc_info{};
40    status = process.get_info(ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), nullptr, nullptr);
41    ASSERT_EQ(ZX_OK, status);
42    return proc_info.return_code;
43}
44
45static bool spawn_control_test(void) {
46    BEGIN_TEST;
47
48    zx_status_t status;
49    zx::process process;
50
51    {
52        const char* argv[] = {kSpawnChild, nullptr};
53        status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
54                            kSpawnChild, argv, process.reset_and_get_address());
55        ASSERT_EQ(ZX_OK, status);
56        EXPECT_EQ(43, join(process));
57    }
58
59    {
60        const char* argv[] = {kSpawnChild, "--argc", nullptr};
61        status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
62                            kSpawnChild, argv, process.reset_and_get_address());
63        ASSERT_EQ(ZX_OK, status);
64        EXPECT_EQ(2, join(process));
65    }
66
67    {
68        const char* argv[] = {kSpawnChild, "--argc", "three", "four", "five", nullptr};
69        status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
70                            kSpawnChild, argv, process.reset_and_get_address());
71        ASSERT_EQ(ZX_OK, status);
72        EXPECT_EQ(5, join(process));
73    }
74
75    END_TEST;
76}
77
78static bool spawn_launcher_test(void) {
79    BEGIN_TEST;
80
81    zx_status_t status;
82    zx::process process;
83    const char* argv[] = {kSpawnLauncher, kSpawnChild, nullptr};
84
85    // Check that we can spawn the lancher process in a job and that the
86    // launcher process can launch the child.
87    {
88        zx::job job;
89        ASSERT_EQ(ZX_OK, zx::job::create(*zx::job::default_job(), 0, &job));
90
91        status = fdio_spawn(job.get(), FDIO_SPAWN_CLONE_ALL, kSpawnLauncher,
92                            argv, process.reset_and_get_address());
93        ASSERT_EQ(ZX_OK, status);
94        EXPECT_EQ(43, join(process));
95        ASSERT_EQ(ZX_OK, job.kill());
96    }
97
98    // Check that setting |ZX_POL_NEW_PROCESS| to |ZX_POL_ACTION_DENY| prevents
99    // the launcher from launching the child.
100    {
101        zx::job job;
102        ASSERT_EQ(ZX_OK, zx::job::create(*zx::job::default_job(), 0, &job));
103        zx_policy_basic_t policy = {
104            .condition = ZX_POL_NEW_PROCESS,
105            .policy = ZX_POL_ACTION_DENY,
106        };
107        ASSERT_EQ(ZX_OK, job.set_policy(ZX_JOB_POL_RELATIVE, ZX_JOB_POL_BASIC, &policy, 1));
108
109        status = fdio_spawn(job.get(), FDIO_SPAWN_CLONE_ALL, kSpawnLauncher,
110                            argv, process.reset_and_get_address());
111        ASSERT_EQ(ZX_OK, status);
112        EXPECT_EQ(401, join(process));
113        ASSERT_EQ(ZX_OK, job.kill());
114    }
115
116    END_TEST;
117}
118
119static bool spawn_invalid_args_test(void) {
120    BEGIN_TEST;
121
122    zx_status_t status;
123    zx::process process;
124
125    const char* argv[] = {kSpawnChild, nullptr};
126
127    status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
128                        "/bogus/not/a/file", argv, process.reset_and_get_address());
129    ASSERT_EQ(ZX_ERR_IO, status);
130
131    status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
132                        kSpawnChild, NULL, process.reset_and_get_address());
133    ASSERT_EQ(ZX_ERR_INVALID_ARGS, status);
134
135    status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
136                        kSpawnChild, argv + 1, process.reset_and_get_address());
137    ASSERT_EQ(ZX_ERR_INVALID_ARGS, status);
138
139    END_TEST;
140}
141
142static bool spawn_flags_test(void) {
143    BEGIN_TEST;
144
145    zx_status_t status;
146    zx::process process;
147
148    {
149        // We can't actually launch a process without FDIO_SPAWN_CLONE_LDSVC
150        // because we can't load the PT_INTERP.
151        const char* argv[] = {kSpawnChild, "--flags", "none", nullptr};
152        status = fdio_spawn(ZX_HANDLE_INVALID, 0, kSpawnChild, argv,
153                            process.reset_and_get_address());
154        ASSERT_EQ(ZX_ERR_INVALID_ARGS, status);
155        EXPECT_FALSE(process.is_valid());
156    }
157
158    {
159        const char* argv[] = {kSpawnChild, "--flags", "none", nullptr};
160        status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_LDSVC,
161                            kSpawnChild, argv, process.reset_and_get_address());
162        ASSERT_EQ(ZX_OK, status);
163        EXPECT_EQ(51, join(process));
164    }
165
166    {
167        const char* argv[] = {kSpawnChild, "--flags", "job", nullptr};
168        status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_CLONE_LDSVC,
169                            kSpawnChild, argv, process.reset_and_get_address());
170        ASSERT_EQ(ZX_OK, status);
171        EXPECT_EQ(52, join(process));
172    }
173
174    {
175        const char* argv[] = {kSpawnChild, "--flags", "namespace", nullptr};
176        status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_LDSVC | FDIO_SPAWN_CLONE_NAMESPACE,
177                            kSpawnChild, argv, process.reset_and_get_address());
178        ASSERT_EQ(ZX_OK, status);
179        EXPECT_EQ(53, join(process));
180    }
181
182    {
183        const char* argv[] = {kSpawnChild, "--flags", "stdio", nullptr};
184        status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_LDSVC | FDIO_SPAWN_CLONE_STDIO,
185                            kSpawnChild, argv, process.reset_and_get_address());
186        ASSERT_EQ(ZX_OK, status);
187        EXPECT_EQ(54, join(process));
188    }
189
190    {
191        const char* argv[] = {kSpawnChild, "--flags", "environ", nullptr};
192        status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_LDSVC | FDIO_SPAWN_CLONE_ENVIRON,
193                            kSpawnChild, argv, process.reset_and_get_address());
194        ASSERT_EQ(ZX_OK, status);
195        EXPECT_EQ(55, join(process));
196    }
197
198    {
199        const char* argv[] = {kSpawnChild, "--flags", "all", nullptr};
200        status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
201                            kSpawnChild, argv, process.reset_and_get_address());
202        ASSERT_EQ(ZX_OK, status);
203        EXPECT_EQ(56, join(process));
204    }
205
206    END_TEST;
207}
208
209static bool spawn_environ_test(void) {
210    BEGIN_TEST;
211
212    zx_status_t status;
213    zx::process process;
214
215    setenv("SPAWN_TEST_PARENT", "1", 1);
216
217    {
218        const char* argv[] = {kSpawnChild, "--env", "empty", nullptr};
219        const char* env[] = {nullptr};
220        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_LDSVC,
221                                kSpawnChild, argv, env, 0, nullptr,
222                                process.reset_and_get_address(), nullptr);
223        ASSERT_EQ(ZX_OK, status);
224        EXPECT_EQ(61, join(process));
225    }
226
227    {
228        const char* argv[] = {kSpawnChild, "--env", "one", nullptr};
229        const char* env[] = {"SPAWN_TEST_CHILD=1", nullptr};
230        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_LDSVC,
231                                kSpawnChild, argv, env, 0, nullptr,
232                                process.reset_and_get_address(), nullptr);
233        ASSERT_EQ(ZX_OK, status);
234        EXPECT_EQ(62, join(process));
235    }
236
237    {
238        const char* argv[] = {kSpawnChild, "--env", "one", nullptr};
239        const char* env[] = {"SPAWN_TEST_CHILD=1", nullptr};
240        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
241                                kSpawnChild, argv, env, 0, nullptr,
242                                process.reset_and_get_address(), nullptr);
243        ASSERT_EQ(ZX_OK, status);
244        EXPECT_EQ(62, join(process));
245    }
246
247    {
248        const char* argv[] = {kSpawnChild, "--env", "two", nullptr};
249        const char* env[] = {"SPAWN_TEST_CHILD=1", "SPAWN_TEST_CHILD2=1", nullptr};
250        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
251                                kSpawnChild, argv, env, 0, nullptr,
252                                process.reset_and_get_address(), nullptr);
253        ASSERT_EQ(ZX_OK, status);
254        EXPECT_EQ(63, join(process));
255    }
256
257    {
258        const char* argv[] = {kSpawnChild, "--env", "clone", nullptr};
259        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
260                                kSpawnChild, argv, nullptr, 0, nullptr,
261                                process.reset_and_get_address(), nullptr);
262        ASSERT_EQ(ZX_OK, status);
263        EXPECT_EQ(64, join(process));
264    }
265
266    unsetenv("SPAWN_TEST_PARENT");
267
268    END_TEST;
269}
270
271static bool spawn_actions_fd_test(void) {
272    BEGIN_TEST;
273
274    zx_status_t status;
275    zx::process process;
276
277    {
278        const char* argv[] = {nullptr};
279        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
280                                kSpawnChild, argv, nullptr, 0, nullptr,
281                                process.reset_and_get_address(), nullptr);
282        ASSERT_EQ(ZX_ERR_INVALID_ARGS, status);
283    }
284
285    {
286        fdio_spawn_action_t action;
287        action.action = FDIO_SPAWN_ACTION_SET_NAME;
288        action.name.data = "spawn-child-name";
289
290        const char* argv[] = {nullptr};
291        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
292                                kSpawnChild, argv, nullptr, 1, &action,
293                                process.reset_and_get_address(), nullptr);
294        ASSERT_EQ(ZX_OK, status);
295        EXPECT_EQ(42, join(process));
296
297        char name[ZX_MAX_NAME_LEN];
298        ASSERT_EQ(ZX_OK, process.get_property(ZX_PROP_NAME, name, sizeof(name)));
299        EXPECT_TRUE(!strcmp("spawn-child-name", name));
300    }
301
302    {
303        zx_handle_t socket = ZX_HANDLE_INVALID;
304        uint32_t type;
305        int fd = fdio_pipe_half(&socket, &type);
306        ASSERT_GE(fd, 0);
307
308        fdio_spawn_action_t action;
309        action.action = FDIO_SPAWN_ACTION_CLONE_FD;
310        action.fd.local_fd = fd;
311        action.fd.target_fd = 21;
312
313        const char* argv[] = {kSpawnChild, "--action", "clone-fd", nullptr};
314        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
315                                kSpawnChild, argv, nullptr, 1, &action,
316                                process.reset_and_get_address(), nullptr);
317        ASSERT_EQ(ZX_OK, status);
318        EXPECT_EQ(71, join(process));
319        EXPECT_TRUE(has_fd(fd));
320        close(fd);
321        zx_handle_close(socket);
322    }
323
324    {
325        zx::socket socket;
326        uint32_t type;
327        int fd = fdio_pipe_half(socket.reset_and_get_address(), &type);
328        ASSERT_GE(fd, 0);
329
330        fdio_spawn_action_t action;
331        action.action = FDIO_SPAWN_ACTION_TRANSFER_FD;
332        action.fd.local_fd = fd;
333        action.fd.target_fd = 21;
334
335        const char* argv[] = {kSpawnChild, "--action", "transfer-fd", nullptr};
336        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
337                                kSpawnChild, argv, nullptr, 1, &action,
338                                process.reset_and_get_address(), nullptr);
339        ASSERT_EQ(ZX_OK, status);
340        EXPECT_EQ(72, join(process));
341        EXPECT_FALSE(has_fd(fd));
342    }
343
344    {
345        zx::socket socket;
346        uint32_t type;
347        int fd = fdio_pipe_half(socket.reset_and_get_address(), &type);
348        ASSERT_GE(fd, 0);
349
350        fdio_spawn_action_t actions[2];
351        actions[0].action = FDIO_SPAWN_ACTION_CLONE_FD;
352        actions[0].fd.local_fd = fd;
353        actions[0].fd.target_fd = 21;
354        actions[1].action = FDIO_SPAWN_ACTION_TRANSFER_FD;
355        actions[1].fd.local_fd = fd;
356        actions[1].fd.target_fd = 22;
357
358        const char* argv[] = {kSpawnChild, "--action", "clone-and-transfer-fd", nullptr};
359        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
360                                kSpawnChild, argv, nullptr, 2, actions,
361                                process.reset_and_get_address(), nullptr);
362        ASSERT_EQ(ZX_OK, status);
363        EXPECT_EQ(73, join(process));
364        EXPECT_FALSE(has_fd(fd));
365    }
366
367    END_TEST;
368}
369
370static bool spawn_actions_ns_test(void) {
371    BEGIN_TEST;
372
373    zx_status_t status;
374    zx::process process;
375
376    {
377        zx::channel h1, h2;
378        ASSERT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2));
379
380        fdio_spawn_action_t action;
381        action.action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY;
382        action.ns.prefix = "/foo/bar/baz";
383        action.ns.handle = h1.release();
384
385        const char* argv[] = {kSpawnChild, "--action", "ns-entry", nullptr};
386        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
387                                kSpawnChild, argv, nullptr, 1, &action,
388                                process.reset_and_get_address(), nullptr);
389        ASSERT_EQ(ZX_OK, status);
390        EXPECT_EQ(74, join(process));
391    }
392
393    END_TEST;
394}
395
396static bool spawn_actions_h_test(void) {
397    BEGIN_TEST;
398
399    zx_status_t status;
400    zx::process process;
401
402    {
403        zx::channel h1, h2;
404        ASSERT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2));
405
406        fdio_spawn_action_t action;
407        action.action = FDIO_SPAWN_ACTION_ADD_HANDLE;
408        action.h.id = PA_USER0;
409        action.h.handle = h1.release();
410
411        const char* argv[] = {kSpawnChild, "--action", "add-handle", nullptr};
412        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
413                                kSpawnChild, argv, nullptr, 1, &action,
414                                process.reset_and_get_address(), nullptr);
415        ASSERT_EQ(ZX_OK, status);
416        EXPECT_EQ(75, join(process));
417    }
418
419    END_TEST;
420}
421
422static bool spawn_actions_name_test(void) {
423    BEGIN_TEST;
424
425    zx_status_t status;
426    zx::process process;
427
428    {
429        fdio_spawn_action_t actions[2];
430        actions[0].action = FDIO_SPAWN_ACTION_SET_NAME;
431        actions[0].name.data = "proc-name-0";
432        actions[1].action = FDIO_SPAWN_ACTION_SET_NAME;
433        actions[1].name.data = "proc-name-1";
434
435        const char* argv[] = {kSpawnChild, nullptr};
436        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
437                                kSpawnChild, argv, nullptr, 2, actions,
438                                process.reset_and_get_address(), nullptr);
439        ASSERT_EQ(ZX_OK, status);
440        EXPECT_EQ(43, join(process));
441        char name[ZX_MAX_NAME_LEN];
442        ASSERT_EQ(ZX_OK, process.get_property(ZX_PROP_NAME, name, sizeof(name)));
443        EXPECT_EQ(0, strcmp(name, "proc-name-1"));
444    }
445
446    END_TEST;
447}
448
449static bool spawn_errors_test(void) {
450    BEGIN_TEST;
451
452    zx_status_t status;
453    zx::process process;
454    char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
455    const char* argv[] = {kSpawnChild, nullptr};
456
457    ASSERT_EQ(ZX_ERR_INVALID_ARGS,
458              fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
459                         nullptr, process.reset_and_get_address()));
460
461    ASSERT_EQ(ZX_ERR_INVALID_ARGS,
462              fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
463                             argv, nullptr, 1, nullptr, process.reset_and_get_address(), nullptr));
464
465    {
466        fdio_spawn_action_t action;
467        action.action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY;
468        action.ns.prefix = "/foo/bar/baz";
469        action.ns.handle = ZX_HANDLE_INVALID;
470
471        ASSERT_EQ(ZX_ERR_INVALID_ARGS,
472                fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
473                               argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr));
474    }
475
476    {
477        fdio_spawn_action_t action;
478        action.action = FDIO_SPAWN_ACTION_ADD_HANDLE;
479        action.h.id = PA_USER0;
480        action.h.handle = ZX_HANDLE_INVALID;
481
482        ASSERT_EQ(ZX_ERR_INVALID_ARGS,
483                fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
484                               argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr));
485    }
486
487    {
488        fdio_spawn_action_t action;
489        action.action = FDIO_SPAWN_ACTION_SET_NAME;
490        action.name.data = nullptr;
491
492        ASSERT_EQ(ZX_ERR_INVALID_ARGS,
493                fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
494                               argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr));
495    }
496
497    {
498        const char* empty_argv[] = {nullptr};
499        ASSERT_EQ(ZX_ERR_INVALID_ARGS,
500                  fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
501                             empty_argv, process.reset_and_get_address()));
502    }
503
504    ASSERT_EQ(ZX_ERR_IO,
505              fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, "/bogus/path",
506                             argv, nullptr, 0, nullptr, process.reset_and_get_address(), err_msg));
507    EXPECT_TRUE(strstr(err_msg, "/bogus/path") != nullptr);
508
509    {
510        zx::job job;
511        ASSERT_EQ(ZX_OK, zx::job::default_job()->duplicate(0, &job));
512        ASSERT_EQ(ZX_ERR_ACCESS_DENIED,
513                  fdio_spawn(job.get(), FDIO_SPAWN_CLONE_ALL, kSpawnChild,
514                             argv, process.reset_and_get_address()));
515    }
516
517    {
518        ASSERT_EQ(30, dup2(0, 30));
519        ASSERT_EQ(0, close(0));
520        status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
521                            kSpawnChild, argv, process.reset_and_get_address());
522        ASSERT_EQ(ZX_OK, status);
523        EXPECT_EQ(43, join(process));
524        ASSERT_EQ(0, dup2(30, 0));
525        ASSERT_EQ(0, close(30));
526    }
527
528    {
529        zx::channel h1, h2;
530        ASSERT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2));
531
532        ASSERT_EQ(30, dup2(0, 30));
533        ASSERT_EQ(0, close(0));
534        fdio_t* io = fdio_service_create(h1.release());
535        ASSERT_EQ(0, fdio_bind_to_fd(io, 0, 0));
536        status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
537                            kSpawnChild, argv, process.reset_and_get_address());
538        ASSERT_EQ(ZX_ERR_NOT_SUPPORTED, status);
539        ASSERT_EQ(0, close(0));
540        ASSERT_EQ(0, dup2(30, 0));
541        ASSERT_EQ(0, close(30));
542    }
543
544    {
545        zx::channel h1, h2;
546        ASSERT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2));
547
548        fdio_t* io = fdio_service_create(h1.release());
549        int fd = fdio_bind_to_fd(io, -1, 0);
550        ASSERT_GE(fd, 3);
551
552        fdio_spawn_action_t action;
553        action.action = FDIO_SPAWN_ACTION_CLONE_FD;
554        action.fd.local_fd = fd;
555        action.fd.target_fd = 21;
556
557        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
558                                kSpawnChild, argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr);
559        ASSERT_EQ(ZX_ERR_NOT_SUPPORTED, status);
560        ASSERT_EQ(0, close(fd));
561    }
562
563    {
564        zx::channel h1, h2;
565        ASSERT_EQ(ZX_OK, zx::channel::create(0, &h1, &h2));
566
567        fdio_t* io = fdio_service_create(h1.release());
568        int fd = fdio_bind_to_fd(io, -1, 0);
569        ASSERT_GE(fd, 3);
570
571        fdio_spawn_action_t action;
572        action.action = FDIO_SPAWN_ACTION_TRANSFER_FD;
573        action.fd.local_fd = fd;
574        action.fd.target_fd = 21;
575
576        status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
577                                kSpawnChild, argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr);
578        ASSERT_EQ(ZX_ERR_NOT_SUPPORTED, status);
579        ASSERT_EQ(-1, close(fd));
580    }
581
582    END_TEST;
583}
584
585static bool spawn_vmo_test(void) {
586    BEGIN_TEST;
587
588    zx_status_t status;
589    zx::process process;
590
591    {
592        int fd = open(kSpawnChild, O_RDONLY);
593        ASSERT_GE(fd, 0);
594        zx_handle_t vmo;
595        ASSERT_EQ(ZX_OK, fdio_get_vmo_clone(fd, &vmo));
596        close(fd);
597
598        const char* argv[] = {kSpawnChild, nullptr};
599        status = fdio_spawn_vmo(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
600                                vmo, argv, nullptr, 0, nullptr,
601                                process.reset_and_get_address(), nullptr);
602        ASSERT_EQ(ZX_OK, status);
603        EXPECT_EQ(43, join(process));
604    }
605
606    END_TEST;
607}
608
609BEGIN_TEST_CASE(spawn_tests)
610RUN_TEST(spawn_control_test)
611RUN_TEST(spawn_launcher_test)
612RUN_TEST(spawn_invalid_args_test)
613RUN_TEST(spawn_flags_test)
614RUN_TEST(spawn_environ_test)
615RUN_TEST(spawn_actions_fd_test)
616RUN_TEST(spawn_actions_ns_test)
617RUN_TEST(spawn_actions_h_test)
618RUN_TEST(spawn_actions_name_test)
619RUN_TEST(spawn_errors_test)
620RUN_TEST(spawn_vmo_test)
621END_TEST_CASE(spawn_tests)
622
623int main(int argc, char** argv) {
624    return unittest_run_all_tests(argc, argv) ? 0 : -1;
625}
626