From nobody Thu Apr 2 17:17:23 2026 Received: from mail-pg1-f173.google.com (mail-pg1-f173.google.com [209.85.215.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 28BB8344D9B for ; Wed, 11 Feb 2026 03:29:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780598; cv=none; b=SrpFJp24s1iOi8Xa2oZpAC5u+MA4M9Dd5CTZKnrdAgxcRTwB7mB7YjPR3wIfk5xQIdLg2r47kT9qF8IHqfWFmByNMnuhmq7GDS3uKp8VDxSJ+dXbSkahZtc4ka5/eblCmaPQif1cQZ+RX8pX6/cLeMwtJoLmrQi963UKkq/3gdg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780598; c=relaxed/simple; bh=s85C2OKbKyG80Gcr37rqh3lRTMRzMhKOlhnVwYinKmo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=H/M7f7aZdu5cq4p9QYhAIbXXwMnZWPAQUDX7uaqMmfwPb6+S8PmthIynAxcPdJbpfpkVJ9Cu/JzdTCMhONNA3+aKfYpRF1iNzPDC+a9CWWminP3c5VkUf+/tgvR3xcZTvEM7yFE8xBpKIGT08+tVqmizeBns1ixbvM2SK5B8ruA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Je8ICFxC; arc=none smtp.client-ip=209.85.215.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Je8ICFxC" Received: by mail-pg1-f173.google.com with SMTP id 41be03b00d2f7-c6c444e89bcso582645a12.2 for ; Tue, 10 Feb 2026 19:29:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780593; x=1771385393; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=t3JSMx43NpMgl9bRcYgyX8dtvUWeN3vQot3jC8EV0N4=; b=Je8ICFxCSo7Mny16fvuG9JIfGZKxjcJBLvp55eRv2reQQuLw0OWxUla9snJfB8+JmJ E0wj7Xs6vtwEmswuqb/16RescIFQOt/4RHZDXLq92aC396CtEnodaAigPAT8Jg3ax2Vz zhS5IlqeI20DsIkUhbSVF0z0044g85ebh+/BR1ofvmZXzlhX3J9lNtkibXHubLi36DHX 6zozR56q5rxVyCejsm7dnlkatGiDu6e7REdgPCQHJlzGGPV6Ph94E9hWsVzlHvWyyUVW 2nYBy9CCTqhy7+4TlEdm6WMv3JT2HXyJqPatE8eBt2mVjLvTpv3qfPdXVM7MWK/tMUEE J72A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780593; x=1771385393; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=t3JSMx43NpMgl9bRcYgyX8dtvUWeN3vQot3jC8EV0N4=; b=VSPeyaFqfoY3/vLsi23YLxkjFsTU3+HPEcgKKeRlVF9PPKs0r0NAQj/BLcp9M60hPA zp6HjyWE5qI6GCYH/b+tkEIm1a++NMcjY/HqmhYyqKrTiTg2gwAEBQG2NeBgenprkIkv 2A8EHWTsd/yU4pvcl3qtc4t430ueqcuhRO+hmYVyeTjROEUh8METLuPmB0jwXDSUMWLV u5u0NQQru5Uz9SqNAuqzj0VJU5WnOPJ2/9hvpp9wmeWDpPtkW/oU0ccwTf0xz2ebrrdH Yer5+8Cmw7eZ/D/kQCLv3U8dK79SZp3vaPnjIfb/M+14h74fqaz46FO/o5sdh9M39pwN zd4A== X-Forwarded-Encrypted: i=1; AJvYcCX3mTTzK8/w+8bICYezu1M14EVfFz4HnZgd9AJLy9mkrF7OepYZ8KvQMjdB6vwe9RtYE+1X0voQ59xOoZU=@vger.kernel.org X-Gm-Message-State: AOJu0YxG9AWRJG3oJMo8+7TDeCsNEbsSDkbSwG0D6cl4bxR5GsfKiVFv 1t9QLG9+NcAPuI1zPSK3/il2ZZrPvtYX4pp+T/krBe42fJSvxDVZmn33 X-Gm-Gg: AZuq6aLdACk5Xf/jfyi1T+Ltgt0Rexrg4BdJkyw/yGLIfufkipZ+BAf26g4Bdj2xzdO ufkeCDcgdRdtLlJ4yLVdHWPleUT934uw5hA+k9E152Qcuc+x45P1ai46xlr7p9OW9yqlVJ5qlMX iWicw7E9xbDP8nwKuDpkEBN3yXDzLkAeB2475m7ej5oGoVVajGfw68emEg4mEUau/VkeFW5wBLV ENIeS9cySkF0FsFE3ALWGG0RcKYq3EniXXByUIbFX2scvfFtmWs+i33jf8EKDdFeVDJHIadyZBb fkMrEcxd4y2vSwjF++ESSS4kb1a6dED16VhyuephfEoZpF0F+d3tCHMbr6Tv/Hd7ICtfxvHkARC uyJUq8c6+B5rt9WNnDACs3Fp6MEc5NjfFaf8M0qjfVntmQS8o1QyADg3evL8dg2N21/1zlo2UEH pcTM3Nv65aYVGvOTeQ/WphRZZsmlWdIQz3l3O+2PuxCg== X-Received: by 2002:a05:6a21:9ccb:b0:38d:edd4:2fc7 with SMTP id adf61e73a8af0-393ad002927mr16562707637.26.1770780593127; Tue, 10 Feb 2026 19:29:53 -0800 (PST) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-c6e197d63c9sm464856a12.20.2026.02.10.19.29.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:29:52 -0800 (PST) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: bhelgaas@google.com, lukas@wunner.de, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-pci@vger.kernel.org, Jonathan.Cameron@huawei.com, linux-cxl@vger.kernel.org, linux-kernel@vger.kernel.org Cc: alex.gaynor@gmail.com, benno.lossin@proton.me, boqun.feng@gmail.com, a.hindborg@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, tmgross@umich.edu, alistair23@gmail.com, ojeda@kernel.org, wilfred.mallawa@wdc.com, aliceryhl@google.com, Simona Vetter Subject: [RFC v3 01/27] rust: add untrusted data abstraction Date: Wed, 11 Feb 2026 13:29:08 +1000 Message-ID: <20260211032935.2705841-2-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260211032935.2705841-1-alistair.francis@wdc.com> References: <20260211032935.2705841-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Benno Lossin When reading data from userspace, hardware or other external untrusted sources, the data must be validated before it is used for logic within the kernel. This abstraction provides a generic newtype wrapper `Untrusted`; it prevents direct access to the inner type. The only way to use the underlying data is to call `.validate()` on such a value. Doing so utilizes the new `Validate` trait that is responsible for all of the validation logic. This trait gives access to the inner value of `Untrusted` by means of another newtype wrapper `Unvalidated`. In contrast to `Untrusted`, `Unvalidated` allows direct access and additionally provides several helper functions for slices. Having these two different newtype wrappers is an idea from Simona Vetter. It has several benefits: it fully prevents safe access to the underlying value of `Untrusted` without going through the `Validate` API. Additionally, it allows one to grep for validation logic by simply looking for `Unvalidated<`. Any API that reads data from an untrusted source should return `Untrusted` where `T` is the type of the underlying untrusted data. This generic allows other abstractions to return their custom type wrapped by `Untrusted`, signaling to the caller that the data must be validated before use. This allows those abstractions to be used both in a trusted and untrusted manner, increasing their generality. Additionally, using the arbitrary self types feature, APIs can be designed to explicitly read untrusted data: impl MyCustomDataSource { pub fn read(self: &Untrusted) -> &Untrusted<[u8]>; } Cc: Simona Vetter Signed-off-by: Benno Lossin Message-ID: <20240925205244.873020-2-benno.lossin@proton.me> --- rust/kernel/lib.rs | 1 + rust/kernel/validate.rs | 605 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 606 insertions(+) create mode 100644 rust/kernel/validate.rs diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 696f62f85eb5..915499704405 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -154,6 +154,7 @@ pub mod uaccess; #[cfg(CONFIG_USB =3D "y")] pub mod usb; +pub mod validate; pub mod workqueue; pub mod xarray; =20 diff --git a/rust/kernel/validate.rs b/rust/kernel/validate.rs new file mode 100644 index 000000000000..ae0aa20e27b4 --- /dev/null +++ b/rust/kernel/validate.rs @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Types for handling and validating untrusted data. +//! +//! # Overview +//! +//! Untrusted data is marked using the [`Untrusted`] type. See [Rationa= le](#rationale) for the +//! reasons to mark untrusted data throughout the kernel. It is a totally = opaque wrapper, it is not +//! possible to read the data inside; but it is possible to [`Untrusted::w= rite`] into it. +//! +//! The only way to "access" the data inside an [`Untrusted`] is to [`U= ntrusted::validate`] it; +//! turning it into a different form using the [`Validate`] trait. That tr= ait receives the data in +//! the form of [`Unvalidated`], which in contrast to [`Untrusted`],= allows access to the +//! underlying data. It additionally provides several utility functions to= simplify validation. +//! +//! # Rationale +//! +//! When reading data from an untrusted source, it must be validated befor= e it can be used for +//! logic. For example, this is a very bad idea: +//! +//! ``` +//! # fn read_bytes_from_network() -> Box<[u8]> { +//! # Box::new([1, 0], kernel::alloc::flags::GFP_KERNEL).unwrap() +//! # } +//! let bytes: Box<[u8]> =3D read_bytes_from_network(); +//! let data_index =3D bytes[0]; +//! let data =3D bytes[usize::from(data_index)]; +//! ``` +//! +//! While this will not lead to a memory violation (because the array inde= x checks the bounds), it +//! might result in a kernel panic. For this reason, all untrusted data mu= st be wrapped in +//! [`Untrusted`]. This type only allows validating the data or passing= it along, since copying +//! data from one userspace buffer into another is allowed for untrusted d= ata. + +use crate::prelude::Init; +use core::{ + mem::MaybeUninit, + ops::{Index, IndexMut}, + ptr, slice, +}; + +/// Untrusted data of type `T`. +/// +/// When reading data from userspace, hardware or other external untrusted= sources, the data must +/// be validated before it is used for logic within the kernel. To do so, = the [`validate()`] +/// function exists and uses the [`Validate`] trait. +/// +/// Also see the [module] description. +/// +/// [`validate()`]: Self::validate +/// [module]: self +#[repr(transparent)] +pub struct Untrusted(Unvalidated); + +impl Untrusted { + /// Marks the given value as untrusted. + /// + /// # Examples + /// + /// ``` + /// use kernel::validate::Untrusted; + /// + /// # mod bindings { pub(crate) unsafe fn read_foo_info() -> [u8; 4] {= todo!() } }; + /// fn read_foo_info() -> Untrusted<[u8; 4]> { + /// // SAFETY: just an FFI call without preconditions. + /// Untrusted::new(unsafe { bindings::read_foo_info() }) + /// } + /// ``` + pub fn new(value: T) -> Self + where + T: Sized, + { + Self(Unvalidated::new(value)) + } + + /// Marks the value behind the reference as untrusted. + /// + /// # Examples + /// + /// In this imaginary example there exists the `foo_hardware` struct o= n the C side, as well as + /// a `foo_hardware_read` function that reads some data directly from = the hardware. + /// ``` + /// use kernel::{error, types::Opaque, validate::Untrusted}; + /// use core::ptr; + /// + /// # #[allow(non_camel_case_types)] + /// # mod bindings { + /// # pub(crate) struct foo_hardware; + /// # pub(crate) unsafe fn foo_hardware_read(_foo: *mut foo_hardwa= re, _len: &mut usize) -> *mut u8 { + /// # todo!() + /// # } + /// # } + /// struct Foo(Opaque); + /// + /// impl Foo { + /// fn read(&mut self, mut len: usize) -> Result<&Untrusted<[u8]>>= { + /// // SAFETY: just an FFI call without preconditions. + /// let data: *mut u8 =3D unsafe { bindings::foo_hardware_read= (self.0.get(), &mut len) }; + /// let data =3D error::from_err_ptr(data)?; + /// let data =3D ptr::slice_from_raw_parts(data, len); + /// // SAFETY: `data` returned by `foo_hardware_read` is valid= for reads as long as the + /// // `foo_hardware` object exists. That function updated the + /// let data =3D unsafe { &*data }; + /// Ok(Untrusted::new_ref(data)) + /// } + /// } + /// ``` + pub fn new_ref(value: &T) -> &Self { + let ptr: *const T =3D value; + // CAST: `Self` and `Unvalidated` are `repr(transparent)` and cont= ain a `T`. + let ptr =3D ptr as *const Self; + // SAFETY: `ptr` came from a shared reference valid for `'a`. + unsafe { &*ptr } + } + + /// Marks the value behind the reference as untrusted. + /// + /// # Examples + /// + /// In this imaginary example there exists the `foo_hardware` struct o= n the C side, as well as + /// a `foo_hardware_read` function that reads some data directly from = the hardware. + /// ``` + /// use kernel::{error, types::Opaque, validate::Untrusted}; + /// use core::ptr; + /// + /// # #[allow(non_camel_case_types)] + /// # mod bindings { + /// # pub(crate) struct foo_hardware; + /// # pub(crate) unsafe fn foo_hardware_read(_foo: *mut foo_hardwa= re, _len: &mut usize) -> *mut u8 { + /// # todo!() + /// # } + /// # } + /// struct Foo(Opaque); + /// + /// impl Foo { + /// fn read(&mut self, mut len: usize) -> Result<&mut Untrusted<[u= 8]>> { + /// // SAFETY: just an FFI call without preconditions. + /// let data: *mut u8 =3D unsafe { bindings::foo_hardware_read= (self.0.get(), &mut len) }; + /// let data =3D error::from_err_ptr(data)?; + /// let data =3D ptr::slice_from_raw_parts_mut(data, len); + /// // SAFETY: `data` returned by `foo_hardware_read` is valid= for reads as long as the + /// // `foo_hardware` object exists. That function updated the + /// let data =3D unsafe { &mut *data }; + /// Ok(Untrusted::new_mut(data)) + /// } + /// } + /// ``` + pub fn new_mut(value: &mut T) -> &mut Self { + let ptr: *mut T =3D value; + // CAST: `Self` and `Unvalidated` are `repr(transparent)` and cont= ain a `T`. + let ptr =3D ptr as *mut Self; + // SAFETY: `ptr` came from a mutable reference valid for `'a`. + unsafe { &mut *ptr } + } + + /// Validates and parses the untrusted data. + /// + /// See the [`Validate`] trait on how to implement it. + pub fn validate<'a, V: Validate<&'a Unvalidated>>(&'a self) -> Resu= lt { + V::validate(&self.0) + } + + /// Validates and parses the untrusted data. + /// + /// See the [`Validate`] trait on how to implement it. + pub fn validate_mut<'a, V: Validate<&'a mut Unvalidated>>( + &'a mut self, + ) -> Result { + V::validate(&mut self.0) + } + + /// Sets the underlying untrusted value. + /// + /// # Examples + /// + /// ``` + /// use kernel::validate::Untrusted; + /// + /// let mut untrusted =3D Untrusted::new(42); + /// untrusted.write(24); + /// ``` + pub fn write(&mut self, value: impl Init) { + let ptr: *mut T =3D &mut self.0 .0; + // SAFETY: `ptr` came from a mutable reference and the value is ov= erwritten before it is + // read. + unsafe { ptr::drop_in_place(ptr) }; + // SAFETY: `ptr` came from a mutable reference and the initializer= cannot error. + match unsafe { value.__init(ptr) } { + Ok(()) =3D> {} + Err(_) =3D> unreachable!(), + } + } + + /// Turns a slice of untrusted values into an untrusted slice of value= s. + pub fn transpose_slice(slice: &[Untrusted]) -> &Untrusted<[T]> + where + T: Sized, + { + let ptr =3D slice.as_ptr().cast::(); + // SAFETY: `ptr` and `len` come from the same slice reference. + let slice =3D unsafe { slice::from_raw_parts(ptr, slice.len()) }; + Untrusted::new_ref(slice) + } + + /// Turns a slice of uninitialized, untrusted values into an untrusted= slice of uninitialized + /// values. + pub fn transpose_slice_uninit( + slice: &[MaybeUninit>], + ) -> &Untrusted<[MaybeUninit]> + where + T: Sized, + { + let ptr =3D slice.as_ptr().cast::>(); + // SAFETY: `ptr` and `len` come from the same mutable slice refere= nce. + let slice =3D unsafe { slice::from_raw_parts(ptr, slice.len()) }; + Untrusted::new_ref(slice) + } + + /// Turns a slice of uninitialized, untrusted values into an untrusted= slice of uninitialized + /// values. + pub fn transpose_slice_uninit_mut( + slice: &mut [MaybeUninit>], + ) -> &mut Untrusted<[MaybeUninit]> + where + T: Sized, + { + // CAST: `MaybeUninit` and `MaybeUninit>` have the= same layout. + let ptr =3D slice.as_mut_ptr().cast::>(); + // SAFETY: `ptr` and `len` come from the same mutable slice refere= nce. + let slice =3D unsafe { slice::from_raw_parts_mut(ptr, slice.len())= }; + Untrusted::new_mut(slice) + } +} + +impl Untrusted> { + /// Sets the underlying untrusted value. + /// + /// # Examples + /// + /// ``` + /// use kernel::validate::Untrusted; + /// + /// let mut untrusted =3D Untrusted::new(42); + /// untrusted.write(24); + /// ``` + pub fn write_uninit(&mut self, value: impl Init) -> Result<&m= ut Untrusted, E> { + let ptr: *mut MaybeUninit =3D &mut self.0 .0; + // CAST: `MaybeUninit` is `repr(transparent)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and if `Err` is returned, t= he underlying memory is + // considered uninitialized. + unsafe { value.__init(ptr) }.map(|()| { + let this =3D self.0.raw_mut(); + // SAFETY: we initialized the memory above. + Untrusted::new_mut(unsafe { this.assume_init_mut() }) + }) + } +} + +impl Untrusted<[MaybeUninit]> { + /// Sets the underlying untrusted value. + /// + /// # Examples + /// + /// ``` + /// use kernel::validate::Untrusted; + /// + /// let mut untrusted =3D Untrusted::new(42); + /// untrusted.write(24); + /// ``` + pub fn write_uninit_slice( + &mut self, + value: impl Init<[T], E>, + ) -> Result<&mut Untrusted<[T]>, E> { + let ptr: *mut [MaybeUninit] =3D &mut self.0 .0; + // CAST: `MaybeUninit` is `repr(transparent)`. + let ptr =3D ptr as *mut [T]; + // SAFETY: `ptr` came from a reference and if `Err` is returned, t= he underlying memory is + // considered uninitialized. + unsafe { value.__init(ptr) }.map(|()| { + let this =3D self.0.raw_mut().as_mut_ptr(); + // CAST: `MaybeUninit` is `repr(transparent)`. + let this =3D this.cast::(); + // SAFETY: `this` and `len` came from the same slice reference. + let this =3D unsafe { slice::from_raw_parts_mut(this, self.0.l= en()) }; + Untrusted::new_mut(this) + }) + } +} + +/// Marks types that can be used as input to [`Validate::validate`]. +pub trait ValidateInput: private::Sealed + Sized {} + +mod private { + pub trait Sealed {} +} + +impl<'a, T: ?Sized> private::Sealed for &'a Unvalidated {} +impl<'a, T: ?Sized> ValidateInput for &'a Unvalidated {} + +impl<'a, T: ?Sized> private::Sealed for &'a mut Unvalidated {} +impl<'a, T: ?Sized> ValidateInput for &'a mut Unvalidated {} + +/// Validates untrusted data. +/// +/// # Examples +/// +/// The simplest way to validate data is to just implement `Validate<&Unva= lidated<[u8]>>` for the +/// type that you wish to validate: +/// +/// ``` +/// use kernel::{ +/// error::{code::EINVAL, Error}, +/// str::{CStr, CString}, +/// validate::{Unvalidated, Validate}, +/// }; +/// +/// struct Data { +/// flags: u8, +/// name: CString, +/// } +/// +/// impl Validate<&Unvalidated<[u8]>> for Data { +/// type Err =3D Error; +/// +/// fn validate(unvalidated: &Unvalidated<[u8]>) -> Result { +/// let raw =3D unvalidated.raw(); +/// let (&flags, name) =3D raw.split_first().ok_or(EINVAL)?; +/// let name =3D CStr::from_bytes_with_nul(name)?.to_cstring()?; +/// Ok(Data { flags, name }) +/// } +/// } +/// ``` +/// +/// This approach copies the data and requires allocation. If you want to = avoid the allocation and +/// copying the data, you can borrow from the input like this: +/// +/// ``` +/// use kernel::{ +/// error::{code::EINVAL, Error}, +/// str::CStr, +/// validate::{Unvalidated, Validate}, +/// }; +/// +/// struct Data<'a> { +/// flags: u8, +/// name: &'a CStr, +/// } +/// +/// impl<'a> Validate<&'a Unvalidated<[u8]>> for Data<'a> { +/// type Err =3D Error; +/// +/// fn validate(unvalidated: &'a Unvalidated<[u8]>) -> Result { +/// let raw =3D unvalidated.raw(); +/// let (&flags, name) =3D raw.split_first().ok_or(EINVAL)?; +/// let name =3D CStr::from_bytes_with_nul(name)?; +/// Ok(Data { flags, name }) +/// } +/// } +/// ``` +/// +/// If you need to in-place validate your data, you currently need to reso= rt to `unsafe`: +/// +/// ``` +/// use kernel::{ +/// error::{code::EINVAL, Error}, +/// str::CStr, +/// validate::{Unvalidated, Validate}, +/// }; +/// use core::mem; +/// +/// // Important: use `repr(C)`, this ensures a linear layout of this type. +/// #[repr(C)] +/// struct Data { +/// version: u8, +/// flags: u8, +/// _reserved: [u8; 2], +/// count: u64, +/// // lots of other fields... +/// } +/// +/// impl Validate<&Unvalidated<[u8]>> for &Data { +/// type Err =3D Error; +/// +/// fn validate(unvalidated: &Unvalidated<[u8]>) -> Result { +/// let raw =3D unvalidated.raw(); +/// if raw.len() < mem::size_of::() { +/// return Err(EINVAL); +/// } +/// // can only handle version 0 +/// if raw[0] !=3D 0 { +/// return Err(EINVAL); +/// } +/// // version 0 only uses the lower 4 bits of flags +/// if raw[1] & 0xf0 !=3D 0 { +/// return Err(EINVAL); +/// } +/// let ptr =3D raw.as_ptr(); +/// // CAST: `Data` only contains integers and has `repr(C)`. +/// let ptr =3D ptr.cast::(); +/// // SAFETY: `ptr` came from a reference and the cast above is v= alid. +/// Ok(unsafe { &*ptr }) +/// } +/// } +/// ``` +/// +/// To be able to modify the parsed data, while still supporting zero-copy= , you can implement +/// `Validate<&mut Unvalidated<[u8]>>`: +/// +/// ``` +/// use kernel::{ +/// error::{code::EINVAL, Error}, +/// str::CStr, +/// validate::{Unvalidated, Validate}, +/// }; +/// use core::mem; +/// +/// // Important: use `repr(C)`, this ensures a linear layout of this type. +/// #[repr(C)] +/// struct Data { +/// version: u8, +/// flags: u8, +/// _reserved: [u8; 2], +/// count: u64, +/// // lots of other fields... +/// } +/// +/// impl Validate<&mut Unvalidated<[u8]>> for &Data { +/// type Err =3D Error; +/// +/// fn validate(unvalidated: &mut Unvalidated<[u8]>) -> Result { +/// let raw =3D unvalidated.raw_mut(); +/// if raw.len() < mem::size_of::() { +/// return Err(EINVAL); +/// } +/// match raw[0] { +/// 0 =3D> {}, +/// 1 =3D> { +/// // version 1 implicitly sets the first bit. +/// raw[1] |=3D 1; +/// }, +/// // can only handle version 0 and 1 +/// _ =3D> return Err(EINVAL), +/// } +/// // version 0 and 1 only use the lower 4 bits of flags +/// if raw[1] & 0xf0 !=3D 0 { +/// return Err(EINVAL); +/// } +/// if raw[1] =3D=3D 0 {} +/// let ptr =3D raw.as_ptr(); +/// // CAST: `Data` only contains integers and has `repr(C)`. +/// let ptr =3D ptr.cast::(); +/// // SAFETY: `ptr` came from a reference and the cast above is v= alid. +/// Ok(unsafe { &*ptr }) +/// } +/// } +/// ``` +pub trait Validate: Sized { + /// Validation error. + type Err; + + /// Validate the given untrusted data and parse it into the output typ= e. + fn validate(unvalidated: I) -> Result; +} + +/// Unvalidated data of type `T`. +#[repr(transparent)] +pub struct Unvalidated(T); + +impl Unvalidated { + fn new(value: T) -> Self + where + T: Sized, + { + Self(value) + } + + fn new_ref(value: &T) -> &Self { + let ptr: *const T =3D value; + // CAST: `Self` is `repr(transparent)` and contains a `T`. + let ptr =3D ptr as *const Self; + // SAFETY: `ptr` came from a mutable reference valid for `'a`. + unsafe { &*ptr } + } + + fn new_mut(value: &mut T) -> &mut Self { + let ptr: *mut T =3D value; + // CAST: `Self` is `repr(transparent)` and contains a `T`. + let ptr =3D ptr as *mut Self; + // SAFETY: `ptr` came from a mutable reference valid for `'a`. + unsafe { &mut *ptr } + } + + /// Validates and parses the untrusted data. + /// + /// See the [`Validate`] trait on how to implement it. + pub fn validate_ref<'a, V: Validate<&'a Unvalidated>>(&'a self) -> = Result { + V::validate(self) + } + + /// Validates and parses the untrusted data. + /// + /// See the [`Validate`] trait on how to implement it. + pub fn validate_mut<'a, V: Validate<&'a mut Unvalidated>>( + &'a mut self, + ) -> Result { + V::validate(self) + } + + /// Gives immutable access to the underlying value. + pub fn raw(&self) -> &T { + &self.0 + } + + /// Gives mutable access to the underlying value. + pub fn raw_mut(&mut self) -> &mut T { + &mut self.0 + } +} + +impl Index for Unvalidated<[T]> +where + I: slice::SliceIndex<[T]>, +{ + type Output =3D Unvalidated; + + fn index(&self, index: I) -> &Self::Output { + Unvalidated::new_ref(self.0.index(index)) + } +} + +impl IndexMut for Unvalidated<[T]> +where + I: slice::SliceIndex<[T]>, +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + Unvalidated::new_mut(self.0.index_mut(index)) + } +} + +/// Immutable unvalidated slice iterator. +pub struct Iter<'a, T>(slice::Iter<'a, T>); + +/// Mutable unvalidated slice iterator. +pub struct IterMut<'a, T>(slice::IterMut<'a, T>); + +impl<'a, T> Iterator for Iter<'a, T> { + type Item =3D &'a Unvalidated; + + fn next(&mut self) -> Option { + self.0.next().map(Unvalidated::new_ref) + } +} + +impl<'a, T> IntoIterator for &'a Unvalidated<[T]> { + type Item =3D &'a Unvalidated; + type IntoIter =3D Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + Iter(self.0.iter()) + } +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item =3D &'a mut Unvalidated; + + fn next(&mut self) -> Option { + self.0.next().map(Unvalidated::new_mut) + } +} + +impl<'a, T> IntoIterator for &'a mut Unvalidated<[T]> { + type Item =3D &'a mut Unvalidated; + type IntoIter =3D IterMut<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + IterMut(self.0.iter_mut()) + } +} + +impl Unvalidated<[T]> { + /// Returns the number of elements in the underlying slice. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns true if the underlying slice has a length of 0. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Iterates over all items and validates each of them individually. + pub fn validate_iter<'a, V: Validate<&'a Unvalidated>>( + &'a self, + ) -> impl Iterator> + 'a { + self.into_iter().map(|item| V::validate(item)) + } + + /// Iterates over all items and validates each of them individually. + pub fn validate_iter_mut<'a, V: Validate<&'a mut Unvalidated>>( + &'a mut self, + ) -> impl Iterator> + 'a { + self.into_iter().map(|item| V::validate(item)) + } +} --=20 2.52.0