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