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