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.
11void 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;
145reply:
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