blob: c6bd8e34cba90ce7cdd88eeb62460b26b77f8cac [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::{
Dispatcher, HandleValue, InitialMutability, ProcessDispatcher, VmObjectDispatcher,
validate_ranged_resource,
};
use crate::user_copy::{UserInOutPtr, UserInPtr, UserOutPtr};
use crate::vm::arch_vm_aspace::{
ARCH_MMU_FLAG_CACHE_MASK, ARCH_MMU_FLAG_CACHED, ARCH_MMU_FLAG_UNCACHED,
ARCH_MMU_FLAG_UNCACHED_DEVICE, ARCH_MMU_FLAG_WRITE_COMBINING,
};
use crate::vm::pmm::{ALLOC_FLAG_ANY, ALLOC_FLAG_CAN_WAIT};
use crate::vm::vm_object::{SupplyOptions, VmObject};
use crate::vm::vm_object_paged::VmObjectPaged;
use crate::vm::vm_page_list::VmPageSpliceList;
use debug::ltracef;
use page;
use pin_init::stack_pin_init;
use syscalls_macro::syscall;
use zx_status::Status;
use zx_types::{
ZX_CACHE_POLICY_CACHED, ZX_CACHE_POLICY_UNCACHED, ZX_CACHE_POLICY_UNCACHED_DEVICE,
ZX_CACHE_POLICY_WRITE_COMBINING, ZX_HANDLE_INVALID, ZX_OBJ_TYPE_VMO,
ZX_POL_AMBIENT_MARK_VMO_EXEC, ZX_POL_NEW_VMO, ZX_RIGHT_DUPLICATE, ZX_RIGHT_EXECUTE,
ZX_RIGHT_GET_PROPERTY, ZX_RIGHT_MAP, ZX_RIGHT_READ, ZX_RIGHT_RESIZE, ZX_RIGHT_SET_PROPERTY,
ZX_RIGHT_WRITE, ZX_RSRC_KIND_SYSTEM, ZX_RSRC_SYSTEM_VMEX_BASE, ZX_VMO_CHILD_NO_WRITE,
ZX_VMO_CHILD_REFERENCE, ZX_VMO_CHILD_RESIZABLE, ZX_VMO_CHILD_SNAPSHOT,
ZX_VMO_CHILD_SNAPSHOT_AT_LEAST_ON_WRITE, ZX_VMO_CHILD_SNAPSHOT_MODIFIED,
};
const LOCAL_TRACE: u32 = 0;
const ZX_CACHE_POLICY_MASK: u32 = 3;
zr::static_assert!(ZX_CACHE_POLICY_CACHED == ARCH_MMU_FLAG_CACHED as u32);
zr::static_assert!(ZX_CACHE_POLICY_UNCACHED == ARCH_MMU_FLAG_UNCACHED as u32);
zr::static_assert!(ZX_CACHE_POLICY_UNCACHED_DEVICE == ARCH_MMU_FLAG_UNCACHED_DEVICE as u32);
zr::static_assert!(ZX_CACHE_POLICY_WRITE_COMBINING == ARCH_MMU_FLAG_WRITE_COMBINING as u32);
zr::static_assert!(ZX_CACHE_POLICY_MASK == ARCH_MMU_FLAG_CACHE_MASK as u32);
#[syscall]
pub fn sys_vmo_create(size: u64, options: u32, out: &mut HandleValue) -> Result<(), Status> {
ltracef!("size {:#x}\n", size);
ProcessDispatcher::with_current(|up| up.enforce_basic_policy(ZX_POL_NEW_VMO))?;
let stats = VmObjectDispatcher::parse_create_syscall_flags(options, size)?;
// create a vm object
let vmo = VmObjectPaged::create(ALLOC_FLAG_ANY | ALLOC_FLAG_CAN_WAIT, stats.flags, stats.size)?;
// create a Vm Object dispatcher
let (kernel_handle, rights) =
VmObjectDispatcher::create(&vmo, size, InitialMutability::Mutable)?;
// create a handle and attach the dispatcher to it
*out = kernel_handle.make_and_add_handle(rights)?;
Ok(())
}
#[syscall]
pub fn sys_vmo_read(
handle: HandleValue,
data: UserOutPtr<u8>,
offset: u64,
len: usize,
) -> Result<(), Status> {
ltracef!(
"handle {:x}, data {:p}, offset {:#x}, len {:#x}\n",
handle.raw_value(),
data.as_ptr(),
offset,
len
);
// lookup the dispatcher from handle
let vmo = Dispatcher::get_with_rights::<VmObjectDispatcher>(handle, ZX_RIGHT_READ)?;
vmo.read(data, offset, len)?;
Ok(())
}
#[syscall]
pub fn sys_vmo_write(
handle: HandleValue,
data: UserInPtr<u8>,
offset: u64,
len: usize,
) -> Result<(), Status> {
ltracef!(
"handle {:x}, data {:p}, offset {:#x}, len {:#x}\n",
handle.raw_value(),
data.as_ptr(),
offset,
len
);
// lookup the dispatcher from handle
let vmo = Dispatcher::get_with_rights::<VmObjectDispatcher>(handle, ZX_RIGHT_WRITE)?;
vmo.write(data, offset, len)?;
Ok(())
}
#[syscall]
pub fn sys_vmo_transfer_data(
dst_vmo_handle: HandleValue,
options: u32,
offset: u64,
length: u64,
src_vmo_handle: HandleValue,
src_offset: u64,
) -> Result<(), Status> {
// Currently, there are no supported options. This may change in the future.
if options != 0 {
return Err(Status::INVALID_ARGS);
}
if !page::is_aligned(offset as usize)
|| !page::is_aligned(length as usize)
|| !page::is_aligned(src_offset as usize)
{
return Err(Status::INVALID_ARGS);
}
let dst_vmo_dispatcher =
Dispatcher::get_with_rights::<VmObjectDispatcher>(dst_vmo_handle, ZX_RIGHT_WRITE)?;
let src_vmo_dispatcher = Dispatcher::get_with_rights::<VmObjectDispatcher>(
src_vmo_handle,
ZX_RIGHT_READ | ZX_RIGHT_WRITE,
)?;
// Short circuit out if src_vmo and dst_vmo are identical and the src_offset is the same as
// the destination offset.
if src_vmo_dispatcher.get_koid() == dst_vmo_dispatcher.get_koid() && src_offset == offset {
return Ok(());
}
stack_pin_init!(let pages = VmPageSpliceList::new());
src_vmo_dispatcher.vmo().take_pages(src_offset, length, pages.as_mut())?;
dst_vmo_dispatcher.vmo().supply_pages(
offset,
length,
pages.as_mut(),
SupplyOptions::TransferData,
)?;
Ok(())
}
#[syscall]
pub fn sys_vmo_get_size(handle: HandleValue, size: UserOutPtr<u64>) -> Result<(), Status> {
ltracef!("handle {:x}, sizep {:p}\n", handle.raw_value(), size.as_ptr());
// lookup the dispatcher from handle
let vmo = Dispatcher::get::<VmObjectDispatcher>(handle)?;
// no rights check, anyone should be able to get the size
// do the operation
let vmo_size = vmo.get_size()?;
size.copy_to_user(&vmo_size)?;
Ok(())
}
#[syscall]
pub fn sys_vmo_get_stream_size(handle: HandleValue, size: UserOutPtr<u64>) -> Result<(), Status> {
// lookup the dispatcher from handle (no rights required to get stream size).
let vmo = Dispatcher::get::<VmObjectDispatcher>(handle)?;
let stream_size = vmo.get_stream_size();
size.copy_to_user(&stream_size)?;
Ok(())
}
#[syscall]
pub fn sys_vmo_set_size(handle: HandleValue, size: u64) -> Result<(), Status> {
ltracef!("handle {:x}, size {:#x}\n", handle.raw_value(), size);
// lookup the dispatcher from handle
let (vmo, rights) =
Dispatcher::get_with_rights_and_actual::<VmObjectDispatcher>(handle, ZX_RIGHT_WRITE)?;
// VMOs that are not resizable should fail with ZX_ERR_UNAVAILABLE for backwards compatibility,
// which will be handled by the SetSize call below. Only validate the RESIZE right if the VMO is
// resizable.
if vmo.vmo().is_resizable() && (rights & ZX_RIGHT_RESIZE) == 0 {
return Err(Status::ACCESS_DENIED);
}
// do the operation
vmo.set_size(size)?;
Ok(())
}
#[syscall]
pub fn sys_vmo_set_stream_size(handle: HandleValue, size: u64) -> Result<(), Status> {
ltracef!("handle {:x}, size {:#x}\n", handle.raw_value(), size);
// lookup the dispatcher from handle
let vmo = Dispatcher::get_with_rights::<VmObjectDispatcher>(handle, ZX_RIGHT_WRITE)?;
// do the operation
vmo.set_stream_size(size)?;
Ok(())
}
#[syscall]
pub fn sys_vmo_op_range(
handle: HandleValue,
op: u32,
offset: u64,
size: u64,
buffer: UserInOutPtr<u8>,
buffer_size: usize,
) -> Result<(), Status> {
ltracef!(
"handle {:x} op {} offset {:#x} size {:#x} buffer {:p} buffer_size {}\n",
handle.raw_value(),
op,
offset,
size,
buffer.as_ptr(),
buffer_size
);
// lookup the dispatcher from handle
// save the rights and pass down into the dispatcher for further testing
let (vmo, rights) = Dispatcher::get_and_rights::<VmObjectDispatcher>(handle)?;
vmo.range_op(op, offset, size, buffer, buffer_size, rights)?;
Ok(())
}
#[syscall]
pub fn sys_vmo_set_cache_policy(handle: HandleValue, cache_policy: u32) -> Result<(), Status> {
// Sanity check the cache policy.
if (cache_policy & !ZX_CACHE_POLICY_MASK) != 0 {
return Err(Status::INVALID_ARGS);
}
// lookup the dispatcher from handle.
let vmo = Dispatcher::get_with_rights::<VmObjectDispatcher>(handle, ZX_RIGHT_MAP)?;
vmo.set_mapping_cache_policy(cache_policy)?;
Ok(())
}
#[syscall]
pub fn sys_vmo_create_child(
handle: HandleValue,
mut options: u32,
offset: u64,
size: u64,
out_handle: &mut HandleValue,
) -> Result<(), Status> {
ltracef!(
"handle {:x} options {:#x} offset {:#x} size {:#x}\n",
handle.raw_value(),
options,
offset,
size
);
let mut no_write = false;
// VMO size is rounded up to the nearest page boundary.
let vmo_size = VmObject::round_size(size)?;
// Resizing a VMO requires the WRITE permissions, but NO_WRITE forbids the WRITE permissions, as
// such it does not make sense to create a VMO with both of these.
if (options & ZX_VMO_CHILD_NO_WRITE) != 0 && (options & ZX_VMO_CHILD_RESIZABLE) != 0 {
return Err(Status::INVALID_ARGS);
}
// Writable is a property of the handle, not the object, so we consume this option here before
// calling create_child.
if (options & ZX_VMO_CHILD_NO_WRITE) != 0 {
no_write = true;
options &= !ZX_VMO_CHILD_NO_WRITE;
}
// Reference children share their size with the parent, so attempts to resize them
// "pass through" and resize the parent. Require ZX_RIGHT_RESIZE on the parent handle to create
// resizable reference children, as they functionally allow the caller to resize parent VMOs.
let would_resize_pass_through =
(options & ZX_VMO_CHILD_RESIZABLE) != 0 && (options & ZX_VMO_CHILD_REFERENCE) != 0;
let desired_rights = ZX_RIGHT_DUPLICATE
| ZX_RIGHT_READ
| if would_resize_pass_through { ZX_RIGHT_RESIZE } else { 0 };
// lookup the dispatcher from handle, save a copy of the rights for later. We must hold onto
// the refptr of this VMO up until we create the dispatcher. The reason for this is that
// VmObjectDispatcher::Create sets the user_id and page_attribution_id in the created child
// vmo. Should the vmo destroyed between creating the child and setting the id in the dispatcher
// the currently unset user_id may be used to re-attribute a parent. Holding the refptr prevents
// any destruction from occurring.
let (vmo, actual_rights) =
Dispatcher::get_with_rights_and_actual::<VmObjectDispatcher>(handle, desired_rights)?;
// clone the vmo into a new one
let child_vmo =
vmo.create_child(options, offset, vmo_size, (actual_rights & ZX_RIGHT_GET_PROPERTY) != 0)?;
// This checks that the child VMO is explicitly created with ZX_VMO_CHILD_SNAPSHOT.
// There are other ways that VMOs can be effectively immutable, for instance if the VMO is
// created with ZX_VMO_CHILD_SNAPSHOT_AT_LEAST_ON_WRITE and meets certain criteria it will be
// "upgraded" to a snapshot. However this behavior is not guaranteed at the API level.
// A choice was made to conservatively only mark VMOs as immutable when the user explicitly
// creates a VMO in a way that is guaranteed at the API level to always output an immutable VMO.
let mut initial_mutability = InitialMutability::Mutable;
if no_write && (options & ZX_VMO_CHILD_SNAPSHOT) != 0 {
initial_mutability = InitialMutability::Immutable;
}
// create a Vm Object dispatcher
let (kernel_handle, default_rights) =
vmo.create_child_dispatcher(&child_vmo, size, options, initial_mutability)?;
// Set the rights to the new handle to no greater than the input (parent) handle minus the
// RESIZE right, which is added independently based on ZX_VMO_CHILD_RESIZABLE; it is possible
// for a non-resizable parent to have a resizable child and vice versa. Always allow
// GET/SET_PROPERTY so the user can set ZX_PROP_NAME on the new clone.
let mut rights = (actual_rights & !ZX_RIGHT_RESIZE)
| if (options & ZX_VMO_CHILD_RESIZABLE) != 0 { ZX_RIGHT_RESIZE } else { 0 }
| ZX_RIGHT_GET_PROPERTY
| ZX_RIGHT_SET_PROPERTY;
// Unless it was explicitly requested to be removed, WRITE can be added to CoW clones at the
// expense of executability.
if no_write {
rights &= !ZX_RIGHT_WRITE;
// NO_WRITE and RESIZABLE cannot be specified together, so we should not have the RESIZE
// right.
debug_assert!((rights & ZX_RIGHT_RESIZE) == 0);
} else if (options
& (ZX_VMO_CHILD_SNAPSHOT
| ZX_VMO_CHILD_SNAPSHOT_AT_LEAST_ON_WRITE
| ZX_VMO_CHILD_SNAPSHOT_MODIFIED))
!= 0
{
rights &= !ZX_RIGHT_EXECUTE;
rights |= ZX_RIGHT_WRITE;
}
// make sure we're somehow not elevating rights beyond what a new vmo should have
debug_assert!(((default_rights | ZX_RIGHT_EXECUTE) & rights) == rights);
// create a handle and attach the dispatcher to it
*out_handle = kernel_handle.make_and_add_handle(rights)?;
Ok(())
}
#[syscall]
pub fn sys_vmo_replace_as_executable(
handle: HandleValue,
vmex: HandleValue,
out: &mut HandleValue,
) -> Result<(), Status> {
ltracef!("repexec {:x} {:x}\n", handle.raw_value(), vmex.raw_value());
let vmex_status = if vmex.raw_value() != ZX_HANDLE_INVALID {
validate_ranged_resource(vmex, ZX_RSRC_KIND_SYSTEM, ZX_RSRC_SYSTEM_VMEX_BASE, 1)
} else {
ProcessDispatcher::with_current(|up| up.enforce_basic_policy(ZX_POL_AMBIENT_MARK_VMO_EXEC))
};
let orig_handle =
ProcessDispatcher::with_current(|up| up.remove_handle(handle)).ok_or(Status::BAD_HANDLE)?;
if orig_handle.dispatcher().get_type() != ZX_OBJ_TYPE_VMO {
return Err(Status::BAD_HANDLE);
}
vmex_status?;
*out = ProcessDispatcher::with_current(|up| {
up.make_and_add_handle_from_ref(
orig_handle.dispatcher(),
orig_handle.rights() | ZX_RIGHT_EXECUTE,
)
})?;
Ok(())
}