blob: f61e2174d9c4aab180b8c3411b62a4083592e15d [file]
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use crate::object::{
ChannelDispatcher, Dispatcher, ExceptionDispatcher, Exceptionate, HandleValue, JobDispatcher,
ProcessDispatcher, ThreadDispatcher,
};
use debug::ltracef;
use syscalls_macro::syscall;
use zx_status::Status;
use zx_types::{
ZX_EXCEPTION_CHANNEL_DEBUGGER, ZX_POL_NEW_CHANNEL, ZX_RIGHT_DUPLICATE, ZX_RIGHT_ENUMERATE,
ZX_RIGHT_INSPECT, ZX_RIGHT_MANAGE_THREAD, ZX_RIGHT_READ, ZX_RIGHT_TRANSFER, ZX_RIGHT_WAIT,
};
const LOCAL_TRACE: u32 = 0;
// zx_status_t zx_task_create_exception_channel
#[syscall]
pub fn sys_task_create_exception_channel(
handle: HandleValue,
options: u32,
out: &mut HandleValue,
) -> Result<(), Status> {
ltracef!("handle {:#x}, options {:#x}\n", handle.raw_value(), options);
if (options & !ZX_EXCEPTION_CHANNEL_DEBUGGER) != 0 {
return Err(Status::INVALID_ARGS);
}
let up = ProcessDispatcher::get_current();
up.enforce_basic_policy(ZX_POL_NEW_CHANNEL)?;
// Required rights to receive exceptions:
// INSPECT: provides non-trivial task information
// DUPLICATE: can create new thread and process handles
// TRANSFER: exceptions or their channels can be transferred
// MANAGE_THREAD: can keep thread paused during exception
// ENUMERATE (job/process): can access child thread (enforced below)
//
// In the future we may want to support some smarter behavior here e.g.
// allowing for exceptions but no task handles if these rights don't exist,
// but to start with we'll keep it simple until we know we want this.
let (task, task_rights) =
up.handle_table().get_dispatcher_with_rights_and_actual::<Dispatcher>(
&up,
handle,
ZX_RIGHT_INSPECT | ZX_RIGHT_DUPLICATE | ZX_RIGHT_TRANSFER | ZX_RIGHT_MANAGE_THREAD,
)?;
// The task handles provided over this exception channel use the rights on
// `handle` so we are sure not to grant any additional rights the caller
// didn't already have.
//
// TODO(https://fxbug.dev/42108165): thread/process/job rights don't always map 1:1.
let mut process_rights = task_rights;
let thread_rights = task_rights;
// Only one of `exceptionate` and `job_to_create_debug_exceptionate` will be `Some`.
//
// When creating debugger exception channel on a job, `exceptionate` will be `None` and
// `job_to_create_debug_exceptionate` is used instead, because
// `JobDispatcher::create_debug_exceptionate` requires dynamic allocation. For other types,
// only `exceptionate` will be used.
let mut exceptionate: Option<&Exceptionate> = None;
let mut job_to_create_debug_exceptionate: Option<&JobDispatcher> = None;
let mut job_or_process = false;
// Use `downcast()` on the borrowed `task` reference to avoid moving the
// `RefPtr` out, as we still need to retain the `RefPtr` to keep our extracted
// `&Exceptionate` alive.
if let Some(job) = task.downcast::<JobDispatcher>() {
if (options & ZX_EXCEPTION_CHANNEL_DEBUGGER) != 0 {
job_to_create_debug_exceptionate = Some(job);
} else {
exceptionate = Some(job.exceptionate());
}
job_or_process = true;
} else if let Some(process) = task.downcast::<ProcessDispatcher>() {
if (options & ZX_EXCEPTION_CHANNEL_DEBUGGER) != 0 {
exceptionate = Some(process.debug_exceptionate());
} else {
exceptionate = Some(process.exceptionate());
}
job_or_process = true;
} else if let Some(thread) = task.downcast::<ThreadDispatcher>() {
if (options & ZX_EXCEPTION_CHANNEL_DEBUGGER) != 0 {
return Err(Status::INVALID_ARGS);
}
// We don't provide access up the task chain, so don't send the process
// handle when we're registering on a thread.
process_rights = 0;
exceptionate = Some(thread.exceptionate());
} else {
return Err(Status::WRONG_TYPE);
}
// For job and process handlers, we require the handle be able to enumerate
// as proof that the caller is allowed to get to the thread handle.
if job_or_process && (task_rights & ZX_RIGHT_ENUMERATE) == 0 {
return Err(Status::ACCESS_DENIED);
}
let (kernel_handle, user_handle, rights) = ChannelDispatcher::create()?;
if let Some(job) = job_to_create_debug_exceptionate {
job.create_debug_exceptionate(kernel_handle, thread_rights, process_rights)?;
} else {
exceptionate.unwrap().set_channel(kernel_handle, thread_rights, process_rights)?;
}
// Strip unwanted rights from the user endpoint, exception channels are
// read-only from userspace.
//
// We don't need to remove the task channel if this fails. Exception
// channels are built to handle the userspace peer closing so it will just
// follow that path if we fail to copy the userspace endpoint out.
*out = up.make_and_add_handle(
user_handle,
rights & (ZX_RIGHT_TRANSFER | ZX_RIGHT_WAIT | ZX_RIGHT_READ),
)?;
Ok(())
}
// zx_status_t zx_exception_get_thread
#[syscall]
pub fn sys_exception_get_thread(
handle: HandleValue,
thread: &mut HandleValue,
) -> Result<(), Status> {
let up = ProcessDispatcher::get_current();
let exception = up.handle_table().get_dispatcher::<ExceptionDispatcher>(&up, handle)?;
let thread_handle = exception.make_thread_handle()?;
*thread = up.handle_table().map_handle_to_value(thread_handle.as_ref());
up.handle_table().add_handle(thread_handle);
Ok(())
}
// zx_status_t zx_exception_get_process
#[syscall]
pub fn sys_exception_get_process(
handle: HandleValue,
process: &mut HandleValue,
) -> Result<(), Status> {
let up = ProcessDispatcher::get_current();
let exception = up.handle_table().get_dispatcher::<ExceptionDispatcher>(&up, handle)?;
let process_handle = exception.make_process_handle()?;
*process = up.handle_table().map_handle_to_value(process_handle.as_ref());
up.handle_table().add_handle(process_handle);
Ok(())
}