From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pg1-f182.google.com (mail-pg1-f182.google.com [209.85.215.182]) (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 7D08238BF72 for ; Tue, 1 Sep 2026 01:04:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.182 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224660; cv=none; b=gvBd15lxC+iQT4UpsA1EyVhpVn5lbh1HC5uWUd/Sw04GhnPjyTIfEfzGKwKK8uNlwp37HcKmF0Gf9St91MoRwAIF1EaueM6KEFktxKDp6m+WvqsHKrKdO14UEpd0PUXl/h3ZrIlUcBlQ0bivmuoYvmBGkoUASZBJE2VdgYOaxDE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788224660; c=relaxed/simple; bh=/K808TfOsG2VgzhfRJeQIUFlgb9kInuGTOzKUhgrVD4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GWyfkm894hQlXa14os6mFiEAt8HnKWK1uS2/K/BsaSXqW1R68jqJJkFZe+oOKMB73i2H1LZ9DE1p44LuyoFS5i+TRxaRgAdtBYm2ASiiCi6qt7bpKQIATDrW9LRPpGkBIM5ac/bQIvvg72InZ2sdMs+cOrJ7tTEOM87xArA0Bng= 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=MFZmBkn9; arc=none smtp.client-ip=209.85.215.182 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="MFZmBkn9" Received: by mail-pg1-f182.google.com with SMTP id 41be03b00d2f7-c96c92c0980so257302a12.3 for ; Mon, 31 Aug 2026 18:04:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788224658; x=1788829458; 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:content-type; bh=65bCRT2r3FA+N3VTYVba2Ft1meiSRlWorEkESgZc4wU=; b=MFZmBkn9ITqAj+D8M4z1VAm/gGyM7yal2PcmNAlFIl0DgcnwBVK8Ua1xLXcAgdAttR /A9/KqjICeKXH4metmRh7+1aNKNZl47nH/Jg7ZJuXsVsUPeyHHZZV4x6e++fy/VVFy7f jdUNUi98GUdDtKFsa7DOYYba5XgoEhRWT/FiL2MANQK5QelH0ye1AA1CER1KvpUhIgKY RwLtpOrfevs0vyTayos3DsYCV+zj3eoD3UFSt64gmn4Ywrdark6gHviCXiz4En1wysp+ dZZZ7fvMny86iHlAKTBEZS4BWgV7OlCZ4xNf2okgCWT7oP1WsMHxw6VpwMZP9ZYbSqAu Hn/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788224658; x=1788829458; 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:content-type; bh=65bCRT2r3FA+N3VTYVba2Ft1meiSRlWorEkESgZc4wU=; b=XN7G6wXzeoPSpUm2qXaKJmFrRdL4hZgTgpPDv3HBFGzXnIhVOOEq4JlyIhJwVMJPow FJEQvLot7ClTyBSpT3J8D0L+5grrd6wxZ4bmvSwha0hvSu+yCBsic5QNnGZFSm5JplEB 3rUFwEU2deIb4KCLrbAvWGnaQuNjc3j68WLx6Pl5HbENZYFe1nPkjOXjUXRU+x/Az6jh l0flIHH2EXXpfa4PiEaUtRbB5elpr+V9ga+Vb/hoJrqWRZfRJtsTN/6Dv54fId/leLi2 Ax0zrfMedRkLcjqVKM53pZC6mTUSYxfj9SzHOk7kk9FDvzlIAeOsiu7WmFM54UXquVJj +BhA== X-Forwarded-Encrypted: i=1; AKwUvBzGdvzmOxthCLCj9VYyeHDHXMKlQhSpaku0I9wy8WPx52aaOJhQMR59AUWSQnAKKJgaaxmjRscR4YIoPDTadw==@vger.kernel.org X-Gm-Message-State: AFuF++kHpHHE/a3+D2pg8aH2js/crbpm7sn+EmHirPv9NnQ1HcRSonLV pXP2frwif8HIoFyJ56bZWPfq2Uk9BRq4fKtuh4Y+NJ1rJyt39REV5N/t+za+yA== X-Gm-Gg: AYBFou1sx4rUlCZ67GffwP7fTsaFB/6WOY06mt3fQyJrdrT6S5ME3n15zmkvJexgThj tueRn339DHbcUVPYjNG9CegsLbwMtE5yseUtjSrHgjUF2lmguYbyco39JjkgU0NX2JDU7Xc3rzc K3OGWx7P3BVafs5CQmhh+7WlaD2+mtTwP8W1ggH5gcUdQYAFAXHsK/craiGb4TDeHtmsG/IWVKU CzYTXL4hTBFWPa6lB/OoAWu+q7IYGdzG7mdhRz6izDEzfxBtmyaHtT9lkN8PoYNrh1j/s1D4/vD 5WOQLtdBi1u3DWyiVebpnQM2YUiWegiPXkKE2D4oWApckvb3AmpHsephGVEmc1uXnQMxnLkyejQ a6FN+T/fyPJXxBj5eOwWQTLIqVhGSiL13sWS4icxiXAH6t+hGF9OPAmUqa1z7c357HrwC12jCfk yVW6MHid7+BN+UBv6bkI3rhzf3CS9cvmw0Qosx5kDoNUjJzF+C7cSLkSuqCxe2lxbwcp9TLbqUA wyodhs/3kqzrvy3 X-Received: by 2002:a17:90a:dfcc:b0:380:f389:447b with SMTP id 98e67ed59e1d1-396d0fd3a24mr48514295a91.11.1788224657525; Mon, 31 Aug 2026 18:04:17 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:13b2:851f:d9cb:44c5]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990d49e9e0sm2372024a91.11.2026.08.31.18.04.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 18:04:16 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: linux-pci@vger.kernel.org, Jonathan.Cameron@huawei.com, djbw@kernel.org, rust-for-linux@vger.kernel.org, lukas@wunner.de, alistair@alistair23.me, jic23@kernel.org, linux-cxl@vger.kernel.org, bhelgaas@google.com, akpm@linux-foundation.org, linux-kernel@vger.kernel.org Cc: gary@garyguo.net, ojeda@kernel.org, benno.lossin@proton.me, a.hindborg@kernel.org, wilfred.mallawa@wdc.com, tmgross@umich.edu, alistair23@gmail.com, boqun.feng@gmail.com, bjorn3_gh@protonmail.com, alex.gaynor@gmail.com, aliceryhl@google.com Subject: [PATCH v3 02/21] rust: create basic untrusted data API Date: Tue, 1 Sep 2026 11:03:28 +1000 Message-ID: <20260901010347.2614656-3-alistair.francis@wdc.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260901010347.2614656-1-alistair.francis@wdc.com> References: <20260901010347.2614656-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Benno Lossin When the kernel receives external data (e.g. from userspace), it usually is a very bad idea to directly use the data for logic decision in the kernel. For this reason, such data should be explicitly marked and validated before making decision based on its value. The `Untrusted` wrapper type marks a value of type `T` as untrusted. The particular meaning of "untrusted" highly depends on the type `T`. For example `T = u8` ensures that the value of the byte cannot be retrieved. However, `T = [u8]` still allows to access the length of the slice. Similarly, `T = KVec` allows modifications. Signed-off-by: Benno Lossin Message-ID: <20250814124424.516191-3-lossin@kernel.org> --- rust/kernel/lib.rs | 1 + rust/kernel/validate.rs | 148 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 rust/kernel/validate.rs diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 4d5c96ddc49c..239f8325b40a 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -143,6 +143,7 @@ pub mod uaccess; #[cfg(CONFIG_USB = "y")] pub mod usb; +pub mod validate; pub mod workqueue; pub mod xarray; diff --git a/rust/kernel/validate.rs b/rust/kernel/validate.rs new file mode 100644 index 000000000000..2b28625c25ef --- /dev/null +++ b/rust/kernel/validate.rs @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Untrusted data API. +//! +//! # Overview +//! +//! Untrusted data is marked using the [`Untrusted`] type. See [Rationale](#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. +//! +//! APIs that write back into userspace usually allow writing untrusted bytes directly, allowing +//! direct copying of untrusted user data back into userspace without validation. +//! +//! # Rationale +//! +//! When reading data from an untrusted source, it must be validated before it can be used for +//! **logic**. For example, this is a very bad idea: +//! +//! ``` +//! # fn read_bytes_from_network() -> KBox<[u8]> { +//! # Box::new([1, 0], kernel::alloc::flags::GFP_KERNEL).unwrap() +//! # } +//! let bytes: KBox<[u8]> = read_bytes_from_network(); +//! let data_index = bytes[0]; +//! let data = bytes[usize::from(data_index)]; +//! ``` +//! +//! While this will not lead to a memory violation (because the array index checks the bounds), it +//! might result in a kernel panic. For this reason, all untrusted data must be wrapped in +//! [`Untrusted`]. This type only allows validating the data or passing it along, since copying +//! data from userspace back into userspace is allowed for untrusted data. + +use core::ops::{Deref, DerefMut}; + +use crate::{ + alloc::{ + Allocator, + Vec, // + }, + transmute::{ + cast_slice, + cast_slice_mut, // + }, +}; + +/// Untrusted data of type `T`. +/// +/// Data coming from userspace is considered untrusted and should be marked by this type. +/// +/// The particular meaning of [`Untrusted`] depends heavily on the type `T`. For example, +/// `&Untrusted<[u8]>` is a reference to an untrusted slice. But the length is not considered +/// untrusted, as it would otherwise violate normal Rust rules. For this reason, one can easily +/// convert that reference to `&[Untrusted]`. Another such example is `Untrusted>`, it +/// derefs to `KVec>`. Raw bytes however do not behave in this way, `Untrusted` is +/// totally opaque. +/// +/// # Usage in API Design +/// +/// The exact location where to put [`Untrusted`] depends on the kind of API. When asking for an +/// untrusted input value, or buffer to write to, always move the [`Untrusted`] wrapper as far +/// inwards as possible: +/// +/// ```ignore +/// // use this +/// pub fn read_from_userspace(buf: &mut [Untrusted]) { todo!() } +/// +/// // and not this +/// pub fn read_from_userspace(buf: &mut Untrusted<[u8]>) { todo!() } +/// ``` +/// +/// The reason for this is that `&mut Untrusted<[u8]>` can beconverted into `&mut [Untrusted]` +/// very easily, but the converse is not possible. +/// +/// For the same reason, when returning untrusted data by-value, one should move the [`Untrusted`] +/// wrapper as far outward as possible: +/// +/// ```ignore +/// // use this +/// pub fn read_all_from_userspace() -> Untrusted> { todo!() } +/// +/// // and not this +/// pub fn read_all_from_userspace() -> KVec> { todo!() } +/// ``` +/// +/// Here too the reason is that `KVec>` is more restrictive compared to +/// `Untrusted>`. +#[repr(transparent)] +pub struct Untrusted(T); + +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(value) + } +} + +impl Deref for Untrusted<[T]> { + type Target = [Untrusted]; + + fn deref(&self) -> &Self::Target { + // SAFETY: `Untrusted` transparently wraps `T`. + unsafe { cast_slice(&self.0) } + } +} + +impl DerefMut for Untrusted<[T]> { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: `Untrusted` transparently wraps `T`. + unsafe { cast_slice_mut(&mut self.0) } + } +} + +impl Deref for Untrusted> { + type Target = Vec, A>; + + fn deref(&self) -> &Self::Target { + let ptr: *const Untrusted> = self; + // CAST: `Untrusted` transparently wraps `T`. + let ptr: *const Vec, A> = ptr.cast(); + // SAFETY: `ptr` is derived from the reference `self`. + unsafe { &*ptr } + } +} + +impl DerefMut for Untrusted> { + fn deref_mut(&mut self) -> &mut Self::Target { + let ptr: *mut Untrusted> = self; + // CAST: `Untrusted` transparently wraps `T`. + let ptr: *mut Vec, A> = ptr.cast(); + // SAFETY: `ptr` is derived from the reference `self`. + unsafe { &mut *ptr } + } +} -- 2.55.0