blob: d183005c6ca238eab6aff1a63f8d5c50b528663b [file]
// Copyright 2026 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
//! Implementation of the `zx_object_get_property` and `zx_object_set_property` syscalls in Rust.
//!
//! Handles property queries and modifications for dispatcher types ported to Rust (e.g. `Dispatcher`,
//! `ProcessDispatcher`, `SocketDispatcher`, `JobDispatcher`), delegating unported or subsystem-specific
//! property queries to C++ FFI helpers.
use crate::object::{Dispatcher, HandleValue, JobDispatcher, ProcessDispatcher, SocketDispatcher};
use crate::user_copy::{UserInPtr, UserOutPtr};
use boot_options::BootOptions;
use core::mem::MaybeUninit;
use syscalls_macro::syscall;
use zerocopy::{FromBytes, Immutable, IntoBytes};
use zx_status::Status;
use zx_types::{
ZX_MAX_NAME_LEN, ZX_PROP_JOB_KILL_ON_OOM, ZX_PROP_NAME, ZX_PROP_PROCESS_BREAK_ON_LOAD,
ZX_PROP_PROCESS_DEBUG_ADDR, ZX_PROP_PROCESS_HW_TRACE_CONTEXT_ID,
ZX_PROP_PROCESS_VDSO_BASE_ADDRESS, ZX_PROP_SOCKET_RX_THRESHOLD, ZX_PROP_SOCKET_TX_THRESHOLD,
ZX_RIGHT_GET_PROPERTY, ZX_RIGHT_SET_PROPERTY, zx_rights_t, zx_status_t,
};
const LOCAL_TRACE: u32 = 0;
#[cfg(target_arch = "x86_64")]
use crate::arch_rs::x86::registers::{X86_MSR_IA32_FS_BASE, X86_MSR_IA32_KERNEL_GS_BASE};
#[cfg(target_arch = "x86_64")]
use zx_types::{ZX_PROP_REGISTER_FS, ZX_PROP_REGISTER_GS};
/// Copies a scalar value `val` of type `T` into the user buffer `value`.
///
/// # Errors
///
/// * [`Status::BUFFER_TOO_SMALL`]: If `size < core::mem::size_of::<T>()`.
fn copy_scalar_to_user<T: IntoBytes + Immutable>(
value: UserOutPtr<u8>,
size: usize,
val: T,
) -> Result<(), Status> {
if size < core::mem::size_of::<T>() {
return Err(Status::BUFFER_TOO_SMALL);
}
value.reinterpret::<T>().write(val)
}
/// Copies a scalar value of type `T` from the user buffer `value`.
///
/// # Errors
///
/// * [`Status::BUFFER_TOO_SMALL`]: If `size < core::mem::size_of::<T>()`.
fn copy_scalar_from_user<T: FromBytes>(value: UserInPtr<u8>, size: usize) -> Result<T, Status> {
if size < core::mem::size_of::<T>() {
return Err(Status::BUFFER_TOO_SMALL);
}
value.reinterpret::<T>().read()
}
/// Validates that the given dispatcher refers to the currently executing thread.
///
/// On x86_64, reading or writing FS/GS base registers via `ZX_PROP_REGISTER_FS` and
/// `ZX_PROP_REGISTER_GS` is restricted to the caller's own current thread.
#[cfg(target_arch = "x86_64")]
fn require_current_thread(dispatcher: &Dispatcher) -> Result<(), Status> {
let thread =
dispatcher.downcast::<crate::object::ThreadDispatcher>().ok_or(Status::WRONG_TYPE)?;
if !thread.is_current() {
return Err(Status::ACCESS_DENIED);
}
Ok(())
}
unsafe extern "C" {
/// C++ fallback for getting properties on dispatcher types not yet ported to Rust.
fn cpp_object_get_property_cpp_types(
dispatcher: *const Dispatcher,
property: u32,
value: *mut core::ffi::c_void,
size: usize,
) -> zx_status_t;
/// C++ fallback for setting properties on dispatcher types not yet ported to Rust.
fn cpp_object_set_property_cpp_types(
dispatcher: *const Dispatcher,
property: u32,
value: *const core::ffi::c_void,
size: usize,
rights: zx_rights_t,
) -> zx_status_t;
}
#[syscall]
pub fn sys_object_get_property(
handle_value: HandleValue,
property: u32,
value: UserOutPtr<u8>,
size: usize,
) -> Result<(), Status> {
if value.is_null() {
return Err(Status::INVALID_ARGS);
}
let dispatcher =
Dispatcher::get_with_rights::<Dispatcher>(handle_value, ZX_RIGHT_GET_PROPERTY)?;
macro_rules! get_scalar_property {
($type:ty, $getter:ident) => {{
let obj = dispatcher.downcast::<$type>().ok_or(Status::WRONG_TYPE)?;
copy_scalar_to_user(value, size, obj.$getter())
}};
}
match property {
ZX_PROP_NAME => {
if size < ZX_MAX_NAME_LEN {
return Err(Status::BUFFER_TOO_SMALL);
}
let mut name = [0u8; ZX_MAX_NAME_LEN];
dispatcher.get_name(&mut name)?;
value.copy_slice_to_user(&name)
}
ZX_PROP_PROCESS_DEBUG_ADDR => get_scalar_property!(ProcessDispatcher, get_debug_addr),
ZX_PROP_PROCESS_BREAK_ON_LOAD => {
get_scalar_property!(ProcessDispatcher, get_dyn_break_on_load)
}
ZX_PROP_PROCESS_VDSO_BASE_ADDRESS => {
get_scalar_property!(ProcessDispatcher, vdso_base_address)
}
ZX_PROP_PROCESS_HW_TRACE_CONTEXT_ID => {
if !BootOptions::get().enable_debugging_syscalls {
return Err(Status::NOT_SUPPORTED);
}
#[cfg(target_arch = "x86_64")]
{
get_scalar_property!(ProcessDispatcher, hw_trace_context_id)
}
#[cfg(not(target_arch = "x86_64"))]
{
Err(Status::NOT_SUPPORTED)
}
}
ZX_PROP_SOCKET_RX_THRESHOLD => get_scalar_property!(SocketDispatcher, get_read_threshold),
ZX_PROP_SOCKET_TX_THRESHOLD => get_scalar_property!(SocketDispatcher, get_write_threshold),
#[cfg(target_arch = "x86_64")]
ZX_PROP_REGISTER_FS | ZX_PROP_REGISTER_GS => {
require_current_thread(&dispatcher)?;
// SAFETY: Reading valid Model Specific Registers on x86_64 hardware.
let val = if property == ZX_PROP_REGISTER_FS {
unsafe { crate::arch_rs::x86::x86::read_msr(X86_MSR_IA32_FS_BASE) }
} else {
unsafe { crate::arch_rs::x86::x86::read_msr(X86_MSR_IA32_KERNEL_GS_BASE) }
};
copy_scalar_to_user(value, size, val as usize)
}
// C++-only dispatchers (e.g. ExceptionDispatcher, StreamDispatcher, VmObjectDispatcher)
_ => {
// SAFETY: Call C++ FFI helper for properties on C++ dispatchers.
let status = unsafe {
cpp_object_get_property_cpp_types(
&*dispatcher,
property,
value.as_ptr() as *mut core::ffi::c_void,
size,
)
};
Status::ok(status)
}
}
}
#[syscall]
pub fn sys_object_set_property(
handle_value: HandleValue,
property: u32,
value: UserInPtr<u8>,
size: usize,
) -> Result<(), Status> {
if value.is_null() {
return Err(Status::INVALID_ARGS);
}
let (dispatcher, rights) = Dispatcher::get_dispatcher_and_rights(handle_value)?;
if (rights & ZX_RIGHT_SET_PROPERTY) == 0 {
return Err(Status::ACCESS_DENIED);
}
macro_rules! set_scalar_property {
($type:ty, $setter:ident) => {{
let obj = dispatcher.downcast::<$type>().ok_or(Status::WRONG_TYPE)?;
let val = copy_scalar_from_user(value, size)?;
obj.$setter(val)
}};
}
match property {
ZX_PROP_NAME => {
let max_len = ZX_MAX_NAME_LEN - 1;
let len = core::cmp::min(size, max_len);
let mut name_buf = [MaybeUninit::uninit(); ZX_MAX_NAME_LEN - 1];
let name_slice = value.copy_slice_from_user(&mut name_buf[..len])?;
dispatcher.set_name(name_slice)
}
#[cfg(target_arch = "x86_64")]
ZX_PROP_REGISTER_FS | ZX_PROP_REGISTER_GS => {
require_current_thread(&dispatcher)?;
let addr = copy_scalar_from_user::<usize>(value, size)?;
if !crate::arch_rs::x86::is_vaddr_canonical(addr as u64) {
return Err(Status::INVALID_ARGS);
}
// SAFETY: Writing canonical virtual address to valid MSR on current thread.
if property == ZX_PROP_REGISTER_FS {
unsafe { crate::arch_rs::x86::x86::write_msr(X86_MSR_IA32_FS_BASE, addr as u64) };
} else {
unsafe {
crate::arch_rs::x86::x86::write_msr(X86_MSR_IA32_KERNEL_GS_BASE, addr as u64)
};
}
Ok(())
}
ZX_PROP_PROCESS_DEBUG_ADDR => {
set_scalar_property!(ProcessDispatcher, set_debug_addr)
}
ZX_PROP_PROCESS_BREAK_ON_LOAD => {
set_scalar_property!(ProcessDispatcher, set_dyn_break_on_load)
}
ZX_PROP_SOCKET_RX_THRESHOLD => {
set_scalar_property!(SocketDispatcher, set_read_threshold)
}
ZX_PROP_SOCKET_TX_THRESHOLD => {
set_scalar_property!(SocketDispatcher, set_write_threshold)
}
ZX_PROP_JOB_KILL_ON_OOM => {
let job = dispatcher.downcast::<JobDispatcher>().ok_or(Status::WRONG_TYPE)?;
let val = copy_scalar_from_user::<usize>(value, size)?;
if val == 0 {
job.set_kill_on_oom(false);
} else if val == 1 {
job.set_kill_on_oom(true);
} else {
return Err(Status::INVALID_ARGS);
}
Ok(())
}
// C++-only dispatchers (e.g. ExceptionDispatcher, StreamDispatcher, VmObjectDispatcher)
_ => {
// SAFETY: Call C++ FFI helper for properties on C++ dispatchers.
let status = unsafe {
cpp_object_set_property_cpp_types(
&*dispatcher,
property,
value.as_ptr() as *const core::ffi::c_void,
size,
rights,
)
};
Status::ok(status)
}
}
}