blob: d00d39d5d9ee1732d1c5782e10841d56cf3d22a8 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "subprocess.h"
#include <lib/backtrace-request/backtrace-request.h>
#include <mini-process/mini-process.h>
// This function is the entire program that the child process will execute. It
// gets directly mapped into the child process via zx_vmo_write() so it,
//
// 1. must not reference any addressable entity outside the function
//
// 2. must fit entirely within its containing VMO
//
// If you find that this program is crashing for no apparent reason, check to
// see if it has outgrown its VMO. See kSizeLimit in mini-process.c.
void minipr_thread_loop(zx_handle_t channel, uintptr_t fnptr) {
if (fnptr == 0) {
// In this mode we don't have a VDSO so we don't care what the handle is
// and therefore we busy-loop. Unless external steps are taken this will
// saturate one core.
volatile uint32_t val = 1;
while (val) {
val += 2u;
}
} else {
// In this mode we do have a VDSO but we are not a real ELF program so
// we need to receive from the parent the address of the syscalls we can
// use. So we can bootstrap, kernel has already transferred the address of
// zx_channel_read() and the handle to one end of the channel which already
// contains a message with the rest of the syscall addresses.
__typeof(zx_channel_read)* read_fn = (__typeof(zx_channel_read)*)fnptr;
uint32_t actual = 0u;
uint32_t actual_handles = 0u;
zx_handle_t original_handle = ZX_HANDLE_INVALID;
minip_ctx_t ctx = {0};
zx_status_t status = (*read_fn)(
channel, 0u, &ctx, &original_handle, sizeof(ctx), 1, &actual, &actual_handles);
if ((status != ZX_OK) || (actual != sizeof(ctx)))
__builtin_trap();
// The received handle in the |ctx| message does not have any use other than
// keeping it alive until the process ends. We basically leak it.
// Acknowledge the initial message.
uint32_t ack[2] = { actual, actual_handles };
status = ctx.channel_write(channel, 0u, ack, sizeof(uint32_t) * 2, NULL, 0u);
if (status != ZX_OK)
__builtin_trap();
do {
// wait for the next message.
status = ctx.object_wait_one(
channel, ZX_CHANNEL_READABLE, ZX_TIME_INFINITE, &actual);
if (status != ZX_OK)
break;
minip_cmd_t cmd = {0};
status = ctx.channel_read(
channel, 0u, &cmd, NULL, sizeof(cmd), 0u, &actual, &actual_handles);
if (status != ZX_OK)
break;
// Execute one or more commands. After each we send a reply with the
// result. If the command does not cause to crash or exit.
uint32_t what = cmd.what;
do {
// This loop is convoluted. A simpler switch() statement
// has the risk of being generated as a table lookup which
// makes it likely it will reference the data section which
// is outside the memory copied to the child.
zx_handle_t handle[2] = {ZX_HANDLE_INVALID, ZX_HANDLE_INVALID};
if (what & MINIP_CMD_ECHO_MSG) {
what &= ~MINIP_CMD_ECHO_MSG;
cmd.status = ZX_OK;
goto reply;
}
if (what & MINIP_CMD_CREATE_EVENT) {
what &= ~MINIP_CMD_CREATE_EVENT;
cmd.status = ctx.event_create(0u, &handle[0]);
goto reply;
}
if (what & MINIP_CMD_CREATE_PROFILE) {
what &= ~MINIP_CMD_CREATE_PROFILE;
// zx_profile_create() needs a handle to the root job, but we don't have one so
// we're passing ZX_HANDLE_INVALID. It is expected that this call will fail.
//
// Note, we're passing NULL instead of a pointer to a properly initialized
// zx_profile_info_t. That's to prevent the compiler from getting smart and
// using a pre-computed structure in the data segment. This function is
// "injected" into the mini-process so there can be no external dependencies.
cmd.status = ctx.profile_create(ZX_HANDLE_INVALID, NULL, &handle[0]);
goto reply;
}
if (what & MINIP_CMD_CREATE_CHANNEL) {
what &= ~MINIP_CMD_CREATE_CHANNEL;
cmd.status = ctx.channel_create(0u, &handle[0], &handle[1]);
goto reply;
}
if (what & MINIP_CMD_USE_BAD_HANDLE_CLOSED) {
what &= ~MINIP_CMD_USE_BAD_HANDLE_CLOSED;
// Test one case of using an invalid handle. This
// tests a double-close of an event handle.
zx_handle_t handle = ZX_HANDLE_INVALID;
if (ctx.event_create(0u, &handle) != ZX_OK ||
ctx.handle_close(handle) != ZX_OK)
__builtin_trap();
cmd.status = ctx.handle_close(handle);
goto reply;
}
if (what & MINIP_CMD_USE_BAD_HANDLE_TRANSFERRED) {
what &= ~MINIP_CMD_USE_BAD_HANDLE_TRANSFERRED;
// Test another case of using an invalid handle. This
// tests closing a handle after it has been transferred
// out of the process (by writing it to a channel). In
// this case, the Handle object still exists inside the
// kernel.
zx_handle_t handle = ZX_HANDLE_INVALID;
zx_handle_t channel1 = ZX_HANDLE_INVALID;
zx_handle_t channel2 = ZX_HANDLE_INVALID;
if (ctx.event_create(0u, &handle) != ZX_OK ||
ctx.channel_create(0u, &channel1, &channel2) != ZX_OK ||
ctx.channel_write(channel1, 0, NULL, 0,
&handle, 1) != ZX_OK)
__builtin_trap();
// This should produce an error and/or exception.
cmd.status = ctx.handle_close(handle);
// Clean up.
if (ctx.handle_close(channel1) != ZX_OK ||
ctx.handle_close(channel2) != ZX_OK)
__builtin_trap();
goto reply;
}
if (what & MINIP_CMD_VALIDATE_CLOSED_HANDLE) {
what &= ~MINIP_CMD_VALIDATE_CLOSED_HANDLE;
zx_handle_t event = ZX_HANDLE_INVALID;
if (ctx.event_create(0u, &event) != ZX_OK)
__builtin_trap();
ctx.handle_close(event);
cmd.status = ctx.object_get_info(
event, ZX_INFO_HANDLE_VALID, NULL, 0, NULL, NULL);
goto reply;
}
if (what & MINIP_CMD_CREATE_PAGER_VMO) {
what &= ~MINIP_CMD_CREATE_PAGER_VMO;
zx_handle_t pager = ZX_HANDLE_INVALID;
if (ctx.pager_create(0u, &pager) != ZX_OK)
__builtin_trap();
zx_handle_t port = ZX_HANDLE_INVALID;
if (ctx.port_create(0u, &port) != ZX_OK)
__builtin_trap();
cmd.status = ctx.pager_create_vmo(pager, 0u, port, 0u, 0u, &handle[0]);
goto reply;
}
if (what & MINIP_CMD_CREATE_VMO_CONTIGUOUS) {
what &= ~MINIP_CMD_CREATE_VMO_CONTIGUOUS;
// This call will fail because we don't have a bti handle, but that's OK because
// we only care about *how* it fails.
cmd.status =
ctx.vmo_contiguous_create(ZX_HANDLE_INVALID, ZX_PAGE_SIZE, 0u, &handle[0]);
goto reply;
}
if (what & MINIP_CMD_CREATE_VMO_PHYSICAL) {
what &= ~MINIP_CMD_CREATE_VMO_PHYSICAL;
// This call will fail because we don't have a mmio resource, but that's OK
// because we only care about *how* it fails.
cmd.status = ctx.vmo_physical_create(ZX_HANDLE_INVALID, 0u, 0u, &handle[0]);
goto reply;
}
if (what & MINIP_CMD_CHANNEL_WRITE) {
what &= ~MINIP_CMD_CHANNEL_WRITE;
uint8_t val = 0;
cmd.status = ctx.channel_write(original_handle, 0, &val, 1, NULL, 0u);
goto reply;
}
if (what & MINIP_CMD_BACKTRACE_REQUEST) {
what &= ~MINIP_CMD_BACKTRACE_REQUEST;
backtrace_request();
cmd.status = ZX_OK;
goto reply;
}
// Neither MINIP_CMD_BUILTIN_TRAP nor MINIP_CMD_EXIT_NORMAL send a
// message so the client will get ZX_CHANNEL_PEER_CLOSED.
if (what & MINIP_CMD_BUILTIN_TRAP)
__builtin_trap();
if (what & MINIP_CMD_EXIT_NORMAL)
ctx.process_exit(0);
// Did not match any known message.
cmd.status = ZX_ERR_WRONG_TYPE;
reply:
actual_handles = (handle[0] == ZX_HANDLE_INVALID) ? 0u : 1u;
status = ctx.channel_write(
channel, 0u, &cmd, sizeof(cmd), handle, actual_handles);
// Loop if there are more commands packed in |what|.
} while (what);
} while (status == ZX_OK);
}
__builtin_trap();
}