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