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 <mini-process/mini-process.h>
6
7 #include <dlfcn.h>
8 #include <stdint.h>
9
10 #include <elfload/elfload.h>
11
12 #include <zircon/process.h>
13 #include <zircon/processargs.h>
14 #include <zircon/stack.h>
15 #include <zircon/status.h>
16 #include <zircon/syscalls.h>
17
18 #include "subprocess.h"
19
get_syscall_addr(const void * syscall_fn,uintptr_t vdso_base)20 static void* get_syscall_addr(const void* syscall_fn, uintptr_t vdso_base) {
21 Dl_info dl_info;
22 if (dladdr(syscall_fn, &dl_info) == 0)
23 return 0;
24 return (void*)(vdso_base + ((uintptr_t)dl_info.dli_saddr - (uintptr_t)dl_info.dli_fbase));
25 }
26
write_ctx_message(zx_handle_t channel,uintptr_t vdso_base,zx_handle_t transferred_handle)27 static zx_status_t write_ctx_message(
28 zx_handle_t channel, uintptr_t vdso_base, zx_handle_t transferred_handle) {
29 minip_ctx_t ctx = {
30 .handle_close = get_syscall_addr(&zx_handle_close, vdso_base),
31 .object_wait_one = get_syscall_addr(&zx_object_wait_one, vdso_base),
32 .object_signal = get_syscall_addr(&zx_object_signal, vdso_base),
33 .event_create = get_syscall_addr(&zx_event_create, vdso_base),
34 .channel_create = get_syscall_addr(&zx_channel_create, vdso_base),
35 .channel_read = get_syscall_addr(&zx_channel_read, vdso_base),
36 .channel_write = get_syscall_addr(&zx_channel_write, vdso_base),
37 .process_exit = get_syscall_addr(&zx_process_exit, vdso_base),
38 .object_get_info = get_syscall_addr(&zx_object_get_info, vdso_base)
39 };
40 return zx_channel_write(channel, 0u, &ctx, sizeof(ctx), &transferred_handle, 1u);
41 }
42
43 // Sets up a VMO on the given VMAR which contains the mini process code and space for a stack.
prepare_stack_vmo(zx_handle_t vmar,zx_vaddr_t * stack_base,zx_vaddr_t * sp)44 static zx_status_t prepare_stack_vmo(zx_handle_t vmar, zx_vaddr_t* stack_base, zx_vaddr_t* sp) {
45 // Allocate a single VMO for the child. It doubles as the stack on the top and
46 // as the executable code (minipr_thread_loop()) at the bottom. In theory, actual
47 // stack usage is minimal, like 160 bytes or less.
48 uint64_t stack_size = 16 * 1024u;
49 zx_handle_t stack_vmo = ZX_HANDLE_INVALID;
50 zx_status_t status = zx_vmo_create(stack_size, 0, &stack_vmo);
51 if (status != ZX_OK)
52 return status;
53 // Try to set the name, but ignore any errors since it's purely for
54 // debugging and diagnostics.
55 static const char vmo_name[] = "mini-process:stack";
56 zx_object_set_property(stack_vmo, ZX_PROP_NAME, vmo_name, sizeof(vmo_name));
57
58 // We assume that the code to execute is less than kSizeLimit bytes.
59 const uint32_t kSizeLimit = 1000;
60 status = zx_vmo_write(stack_vmo, &minipr_thread_loop, 0u, kSizeLimit);
61 if (status != ZX_OK)
62 goto exit;
63
64 zx_vm_option_t perms = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE;
65 status = zx_vmar_map(vmar, perms, 0, stack_vmo, 0, stack_size, stack_base);
66 if (status != ZX_OK)
67 goto exit;
68
69 // Compute a valid starting SP for the machine's ABI.
70 *sp = compute_initial_stack_pointer(*stack_base, stack_size);
71
72 exit:
73 // Close the VMO handle no matter what; if we failed we want to release it, and if
74 // we succeeded the VMAR retains a reference to it so we don't need a handle.
75 zx_handle_close(stack_vmo);
76 return status;
77 }
78
start_mini_process_etc(zx_handle_t process,zx_handle_t thread,zx_handle_t vmar,zx_handle_t transferred_handle,zx_handle_t * control_channel)79 zx_status_t start_mini_process_etc(zx_handle_t process, zx_handle_t thread,
80 zx_handle_t vmar,
81 zx_handle_t transferred_handle,
82 zx_handle_t* control_channel) {
83 zx_vaddr_t stack_base = 0;
84 zx_vaddr_t sp = 0;
85 zx_status_t status = prepare_stack_vmo(vmar, &stack_base, &sp);
86 if (status != ZX_OK)
87 return status;
88
89 zx_handle_t chn[2] = { ZX_HANDLE_INVALID, ZX_HANDLE_INVALID };
90
91
92 if (!control_channel) {
93 // Simple mode /////////////////////////////////////////////////////////////
94 // Don't map the VDSO, so the only thing the mini-process can do is busy-loop.
95 // The handle sent to the process is just the caller's handle.
96 status = zx_process_start(process, thread, stack_base, sp, transferred_handle, 0);
97 transferred_handle = ZX_HANDLE_INVALID;
98
99 } else {
100 // Complex mode ////////////////////////////////////////////////////////////
101 // The mini-process is going to run a simple request-response over a channel
102 // So we need to:
103 // 1- map the VDSO in the child process, without launchpad.
104 // 2- create a channel and give one end to the child process.
105 // 3- send a message with the rest of the syscall function addresses.
106 // 4- wait for reply.
107
108 status = zx_channel_create(0u, &chn[0], &chn[1]);
109 if (status != ZX_OK)
110 goto exit;
111
112 // This is not thread-safe. It steals the startup handle, so it's not
113 // compatible with also using launchpad (which also needs to steal the
114 // startup handle).
115 static zx_handle_t vdso_vmo = ZX_HANDLE_INVALID;
116 if (vdso_vmo == ZX_HANDLE_INVALID) {
117 vdso_vmo = zx_take_startup_handle(PA_HND(PA_VMO_VDSO, 0));
118 if (vdso_vmo == ZX_HANDLE_INVALID) {
119 status = ZX_ERR_INTERNAL;
120 goto exit;
121 }
122 }
123
124 uintptr_t vdso_base = 0;
125 elf_load_header_t header;
126 uintptr_t phoff;
127 zx_status_t status = elf_load_prepare(vdso_vmo, NULL, 0,
128 &header, &phoff);
129 if (status == ZX_OK) {
130 elf_phdr_t phdrs[header.e_phnum];
131 status = elf_load_read_phdrs(vdso_vmo, phdrs, phoff,
132 header.e_phnum);
133 if (status == ZX_OK)
134 status = elf_load_map_segments(vmar, &header, phdrs, vdso_vmo,
135 NULL, &vdso_base, NULL);
136 }
137 if (status != ZX_OK)
138 goto exit;
139
140 status = write_ctx_message(chn[0], vdso_base, transferred_handle);
141 transferred_handle = ZX_HANDLE_INVALID;
142 if (status != ZX_OK)
143 goto exit;
144
145 uintptr_t channel_read = (uintptr_t)get_syscall_addr(&zx_channel_read, vdso_base);
146
147 status = zx_process_start(process, thread, stack_base, sp, chn[1], channel_read);
148 chn[1] = ZX_HANDLE_INVALID;
149 if (status != ZX_OK)
150 goto exit;
151
152 uint32_t observed;
153 status = zx_object_wait_one(chn[0],
154 ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, ZX_TIME_INFINITE, &observed);
155
156 if (observed & ZX_CHANNEL_PEER_CLOSED) {
157 // the child process died prematurely.
158 status = ZX_ERR_UNAVAILABLE;
159 goto exit;
160 }
161
162 if (observed & ZX_CHANNEL_READABLE) {
163 uint32_t ack[2];
164 uint32_t actual_handles;
165 uint32_t actual_bytes;
166 status = zx_channel_read(
167 chn[0], 0u, ack, NULL, sizeof(uint32_t) * 2, 0u, &actual_bytes, &actual_handles);
168 }
169
170 *control_channel = chn[0];
171 chn[0] = ZX_HANDLE_INVALID;
172 }
173
174 exit:
175 if (transferred_handle != ZX_HANDLE_INVALID)
176 zx_handle_close(transferred_handle);
177 if (chn[0] != ZX_HANDLE_INVALID)
178 zx_handle_close(chn[0]);
179 if (chn[1] != ZX_HANDLE_INVALID)
180 zx_handle_close(chn[1]);
181
182 return status;
183 }
184
mini_process_cmd_send(zx_handle_t cntrl_channel,uint32_t what)185 zx_status_t mini_process_cmd_send(zx_handle_t cntrl_channel, uint32_t what) {
186 minip_cmd_t cmd = {
187 .what = what,
188 .status = ZX_OK
189 };
190
191 return zx_channel_write(cntrl_channel, 0, &cmd, sizeof(cmd), NULL, 0);
192 }
193
mini_process_cmd_read_reply(zx_handle_t cntrl_channel,zx_handle_t * handle)194 zx_status_t mini_process_cmd_read_reply(zx_handle_t cntrl_channel,
195 zx_handle_t* handle) {
196 zx_status_t status = zx_object_wait_one(
197 cntrl_channel, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
198 ZX_TIME_INFINITE, NULL);
199 if (status != ZX_OK)
200 return status;
201 minip_cmd_t reply;
202 uint32_t handle_count = handle ? 1 : 0;
203 uint32_t actual_bytes = 0;
204 uint32_t actual_handles = 0;
205 status = zx_channel_read(cntrl_channel, 0, &reply, handle, sizeof(reply),
206 handle_count, &actual_bytes, &actual_handles);
207 if (status != ZX_OK)
208 return status;
209 return reply.status;
210 }
211
mini_process_cmd(zx_handle_t cntrl_channel,uint32_t what,zx_handle_t * handle)212 zx_status_t mini_process_cmd(zx_handle_t cntrl_channel, uint32_t what, zx_handle_t* handle) {
213 zx_status_t status = mini_process_cmd_send(cntrl_channel, what);
214 if (status != ZX_OK)
215 return status;
216 return mini_process_cmd_read_reply(cntrl_channel, handle);
217 }
218
start_mini_process(zx_handle_t job,zx_handle_t transferred_handle,zx_handle_t * process,zx_handle_t * thread)219 zx_status_t start_mini_process(zx_handle_t job, zx_handle_t transferred_handle,
220 zx_handle_t* process, zx_handle_t* thread) {
221 *process = ZX_HANDLE_INVALID;
222 zx_handle_t vmar = ZX_HANDLE_INVALID;
223 zx_handle_t channel = ZX_HANDLE_INVALID;
224
225 zx_status_t status = zx_process_create(job, "minipr", 6u, 0u, process, &vmar);
226 if (status != ZX_OK)
227 goto exit;
228
229 *thread = ZX_HANDLE_INVALID;
230 status = zx_thread_create(*process, "minith", 6u, 0, thread);
231 if (status != ZX_OK)
232 goto exit;
233
234 status = start_mini_process_etc(*process, *thread, vmar, transferred_handle, &channel);
235 transferred_handle = ZX_HANDLE_INVALID; // The transferred_handle gets consumed.
236 exit:
237 if (status != ZX_OK) {
238 if (transferred_handle != ZX_HANDLE_INVALID)
239 zx_handle_close(transferred_handle);
240 if (*process != ZX_HANDLE_INVALID)
241 zx_handle_close(*process);
242 if (*thread != ZX_HANDLE_INVALID)
243 zx_handle_close(*thread);
244 }
245
246 if (channel != ZX_HANDLE_INVALID)
247 zx_handle_close(channel);
248
249 return status;
250 }
251
start_mini_process_thread(zx_handle_t thread,zx_handle_t vmar)252 zx_status_t start_mini_process_thread(zx_handle_t thread, zx_handle_t vmar) {
253 zx_vaddr_t stack_base = 0;
254 zx_vaddr_t sp = 0;
255 zx_status_t status = prepare_stack_vmo(vmar, &stack_base, &sp);
256 if (status != ZX_OK)
257 return status;
258
259 return zx_thread_start(thread, stack_base, sp, 0, 0);
260 }
261