1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "subprocess.h"
6 #include <mini-process/mini-process.h>
7
8 // This function is the entire program that the child process will execute. It
9 // gets directly mapped into the child process via zx_vmo_write() so it must not
10 // reference any addressable entity outside it.
minipr_thread_loop(zx_handle_t channel,uintptr_t fnptr)11 void minipr_thread_loop(zx_handle_t channel, uintptr_t fnptr) {
12 if (fnptr == 0) {
13 // In this mode we don't have a VDSO so we don't care what the handle is
14 // and therefore we busy-loop. Unless external steps are taken this will
15 // saturate one core.
16 volatile uint32_t val = 1;
17 while (val) {
18 val += 2u;
19 }
20 } else {
21 // In this mode we do have a VDSO but we are not a real ELF program so
22 // we need to receive from the parent the address of the syscalls we can
23 // use. So we can bootstrap, kernel has already transferred the address of
24 // zx_channel_read() and the handle to one end of the channel which already
25 // contains a message with the rest of the syscall addresses.
26 __typeof(zx_channel_read)* read_fn = (__typeof(zx_channel_read)*)fnptr;
27
28 uint32_t actual = 0u;
29 uint32_t actual_handles = 0u;
30 zx_handle_t handle[2];
31 minip_ctx_t ctx;
32
33 zx_status_t status = (*read_fn)(
34 channel, 0u, &ctx, handle, sizeof(ctx), 1, &actual, &actual_handles);
35 if ((status != ZX_OK) || (actual != sizeof(ctx)))
36 __builtin_trap();
37
38 // The received handle in the |ctx| message does not have any use other than
39 // keeping it alive until the process ends. We basically leak it.
40
41 // Acknowledge the initial message.
42 uint32_t ack[2] = { actual, actual_handles };
43 status = ctx.channel_write(channel, 0u, ack, sizeof(uint32_t) * 2, NULL, 0u);
44 if (status != ZX_OK)
45 __builtin_trap();
46
47 do {
48 // wait for the next message.
49 status = ctx.object_wait_one(
50 channel, ZX_CHANNEL_READABLE, ZX_TIME_INFINITE, &actual);
51 if (status != ZX_OK)
52 break;
53
54 minip_cmd_t cmd;
55 status = ctx.channel_read(
56 channel, 0u, &cmd, NULL, sizeof(cmd), 0u, &actual, &actual_handles);
57
58 // Execute one or more commands. After each we send a reply with the
59 // result. If the command does not cause to crash or exit.
60 uint32_t what = cmd.what;
61
62 do {
63 // This loop is convoluted. A simpler switch() statement
64 // has the risk of being generated as a table lookup which
65 // makes it likely it will reference the data section which
66 // is outside the memory copied to the child.
67
68 handle[0] = ZX_HANDLE_INVALID;
69 handle[1] = ZX_HANDLE_INVALID;
70
71 if (what & MINIP_CMD_ECHO_MSG) {
72 what &= ~MINIP_CMD_ECHO_MSG;
73 cmd.status = ZX_OK;
74 goto reply;
75 }
76 if (what & MINIP_CMD_CREATE_EVENT) {
77 what &= ~MINIP_CMD_CREATE_EVENT;
78 cmd.status = ctx.event_create(0u, &handle[0]);
79 goto reply;
80 }
81 if (what & MINIP_CMD_CREATE_CHANNEL) {
82 what &= ~MINIP_CMD_CREATE_CHANNEL;
83 cmd.status = ctx.channel_create(0u, &handle[0], &handle[1]);
84 goto reply;
85 }
86 if (what & MINIP_CMD_USE_BAD_HANDLE_CLOSED) {
87 what &= ~MINIP_CMD_USE_BAD_HANDLE_CLOSED;
88
89 // Test one case of using an invalid handle. This
90 // tests a double-close of an event handle.
91 zx_handle_t handle;
92 if (ctx.event_create(0u, &handle) != ZX_OK ||
93 ctx.handle_close(handle) != ZX_OK)
94 __builtin_trap();
95 cmd.status = ctx.handle_close(handle);
96 goto reply;
97 }
98 if (what & MINIP_CMD_USE_BAD_HANDLE_TRANSFERRED) {
99 what &= ~MINIP_CMD_USE_BAD_HANDLE_TRANSFERRED;
100
101 // Test another case of using an invalid handle. This
102 // tests closing a handle after it has been transferred
103 // out of the process (by writing it to a channel). In
104 // this case, the Handle object still exists inside the
105 // kernel.
106 zx_handle_t handle;
107 zx_handle_t channel1;
108 zx_handle_t channel2;
109 if (ctx.event_create(0u, &handle) != ZX_OK ||
110 ctx.channel_create(0u, &channel1, &channel2) != ZX_OK ||
111 ctx.channel_write(channel1, 0, NULL, 0,
112 &handle, 1) != ZX_OK)
113 __builtin_trap();
114 // This should produce an error and/or exception.
115 cmd.status = ctx.handle_close(handle);
116 // Clean up.
117 if (ctx.handle_close(channel1) != ZX_OK ||
118 ctx.handle_close(channel2) != ZX_OK)
119 __builtin_trap();
120 goto reply;
121 }
122 if (what & MINIP_CMD_VALIDATE_CLOSED_HANDLE) {
123 what &= ~MINIP_CMD_VALIDATE_CLOSED_HANDLE;
124
125 zx_handle_t event;
126 if (ctx.event_create(0u, &event) != ZX_OK)
127 __builtin_trap();
128 ctx.handle_close(event);
129 cmd.status = ctx.object_get_info(
130 event, ZX_INFO_HANDLE_VALID, NULL, 0, NULL, NULL);
131 goto reply;
132 }
133
134 // Neither MINIP_CMD_BUILTIN_TRAP nor MINIP_CMD_EXIT_NORMAL send a
135 // message so the client will get ZX_CHANNEL_PEER_CLOSED.
136
137 if (what & MINIP_CMD_BUILTIN_TRAP)
138 __builtin_trap();
139
140 if (what & MINIP_CMD_EXIT_NORMAL)
141 ctx.process_exit(0);
142
143 // Did not match any known message.
144 cmd.status = ZX_ERR_WRONG_TYPE;
145 reply:
146 actual_handles = (handle[0] == ZX_HANDLE_INVALID) ? 0u : 1u;
147 status = ctx.channel_write(
148 channel, 0u, &cmd, sizeof(cmd), handle, actual_handles);
149
150 // Loop if there are more commands packed in |what|.
151 } while (what);
152
153 } while (status == ZX_OK);
154 }
155
156 __builtin_trap();
157 }
158