blob: c0d1b68c277c78cd104c994cdaa0d92a58d27c15 [file]
// Copyright 2021 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
//! Function arguments of types narrower than a 64-bit register are passed in a
//! full 64-bit register or a full 64-bit stack slot. The machine calling
//! conventions either say that the high bits are unspecified, or that the
//! narrow integer value is zero-extended or sign-extended as appropriate to its
//! type or thereabouts, or doesn't specify it clearly.
//!
//! * The aarch64 psABI says high bits are unspecified so the compiler is
//! obliged to ignore them.
//!
//! * The x86-64 psABI doesn't clearly specify it (except that bool values are
//! zero-extended from 1 bit to 8 bits); in observed fact, compilers do
//! sometimes assume incoming register values have no excess high bits set.
//!
//! * The RISC-V psABI says that values of types narrower than 32 bits are
//! extended as appropriate for their type to 32 bits, and then (they and
//! original 32-bit values) are zero-extended to 64 bits.
//!
//! Even in a case like aarch64 where the compiler is unambiguously obliged to
//! ignore the high bits rather than assume they have been correctly zeroed or
//! sign-extended, the kernel should not trust the compiler not to slip up here,
//! nor otherwise allow unintended input noise from the user to have any effect
//! whatsoever on the kernel (ideally, not even littering the spilled values in
//! kernel stack frames differently in case those are accessible via exploits).
//!
//! So this trait handles sanitizing the user argument values into Rust values
//! that are safe to trust the compiler with.
use crate::object::HandleValue;
use crate::user_copy::{UserInOutPtr, UserInPtr, UserOutPtr};
use zx_types::zx_handle_t;
/// A syscall argument type that can be sanitized from the raw 64-bit register value the
/// generated entry-point wrappers receive from user mode.
pub(super) trait SafeSyscallArgument {
/// This returns a safely valid (if not trustworthy) value of type `Self` by
/// sanitizing the incoming value from the user of any excess high bits.
/// This is a direct user value of type `Self` and not to be considered "safe" as
/// an input value, but it is safely actually of type `Self` by Rust semantics
/// rather than potentially having undefined behavior at the language level.
fn sanitize(value: u64) -> Self;
}
/// Implements [`SafeSyscallArgument`] for integer types narrower than the 64-bit register.
macro_rules! impl_narrow_integer_argument {
($($ty:ty),* $(,)?) => {
$(
impl SafeSyscallArgument for $ty {
#[inline(always)]
fn sanitize(value: u64) -> Self {
// This will just truncate the high bits so they are sign- or
// zero-extended from the narrower integer type in the low bits.
value as Self
}
}
)*
};
}
impl_narrow_integer_argument!(u8, u16, u32, i8, i16, i32);
impl SafeSyscallArgument for u64 {
#[inline(always)]
fn sanitize(value: u64) -> Self {
value
}
}
impl SafeSyscallArgument for i64 {
#[inline(always)]
fn sanitize(value: u64) -> Self {
value as Self
}
}
// The register is the width of a pointer, so `usize` and `isize` are used as is.
zr::static_assert!(size_of::<usize>() == size_of::<u64>());
zr::static_assert!(size_of::<isize>() == size_of::<i64>());
impl SafeSyscallArgument for usize {
#[inline(always)]
fn sanitize(value: u64) -> Self {
value as Self
}
}
impl SafeSyscallArgument for isize {
#[inline(always)]
fn sanitize(value: u64) -> Self {
value as Self
}
}
/// `bool` arguments are not currently used, but this would make them safe.
/// (They must still be excluded from struct layouts used via copy-in.)
impl SafeSyscallArgument for bool {
#[inline(always)]
fn sanitize(value: u64) -> Self {
value & 1 != 0
}
}
impl SafeSyscallArgument for HandleValue {
#[inline(always)]
fn sanitize(value: u64) -> Self {
HandleValue::new(zx_handle_t::sanitize(value))
}
}
impl<T> SafeSyscallArgument for UserInPtr<T> {
#[inline(always)]
fn sanitize(value: u64) -> Self {
UserInPtr::new(core::ptr::with_exposed_provenance(usize::sanitize(value)))
}
}
impl<T> SafeSyscallArgument for UserOutPtr<T> {
#[inline(always)]
fn sanitize(value: u64) -> Self {
UserOutPtr::new(core::ptr::with_exposed_provenance_mut(usize::sanitize(value)))
}
}
impl<T> SafeSyscallArgument for UserInOutPtr<T> {
#[inline(always)]
fn sanitize(value: u64) -> Self {
UserInOutPtr::new(core::ptr::with_exposed_provenance_mut(usize::sanitize(value)))
}
}