From patchwork Sun Sep 15 14:31:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13804825 X-Patchwork-Delegate: paul@paul-moore.com Received: from mail-yw1-f201.google.com (mail-yw1-f201.google.com [209.85.128.201]) (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 1F1D01C7B66 for ; Sun, 15 Sep 2024 14:31:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410701; cv=none; b=f5ZiHNjMPoVxx2/LTmsgD3OlUO8vWk/xtL9o+yHB1n/NLrzc7xYJ2g4aGTZw8Q/p6SbDKsiUaMAWV23m6XZYdx7ZwUenkcxtfpjKyPvtyS8TRTCvHFBue2m18v7HE4ZxRDh+QwbavR44ZcTKn5cjCb1DJVVn7Dd2wCbTDQ5T3Zw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410701; c=relaxed/simple; bh=pya4UxUIvqhrpbWbTgUte469le+L6lTszY8lv89yptM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=onDx9aJB794Po7hp5NFCcW+y5m4zLAu/1QVWSyb4e+Gev1JvnFEbZ5WrInryddLi5QNBatYzpH7qBMegcHyJmbHNpVT6GEmqOJdKYA6hAv/ZZFmddQLxDb6FZLEsZHTs0kDeypVYCAYd1yDl5J+QHYRvrbCF0KC0gfm1s4aBbsY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=pUOmdc5Y; arc=none smtp.client-ip=209.85.128.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="pUOmdc5Y" Received: by mail-yw1-f201.google.com with SMTP id 00721157ae682-6d4426ad833so120517847b3.2 for ; Sun, 15 Sep 2024 07:31:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1726410698; x=1727015498; darn=vger.kernel.org; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=oI5g3/Lv8mNk/syaak4D+x2fQixDZyZK48NDDewIoU0=; b=pUOmdc5YAlk6OnGwbdw2c2impum0KEq9Ozacc4QNrEkaSKMrPFf9TpyROXahQ2J/LH NS9vUOMkblAn2edAFywE7svqlAB/BFAdYGUhx+YxIuy02jlOfXyfUkaa9FDPnp/NxWqw n3YtzMCSA4j/iih6Di5T7fm1woeRe24zg0y55diUXuwvdndXx8pHURkp+ju1glPbWglK EZPTvN22nHQ7KGQvSyn0C4ULAecBCeiU1tmCNmMwX4RVY8VQhSYe7ShIO7hyiFC4tQGM 9ts2PBSomKYx/2uLjeNizhJ/L9ZNszpaBFPGwO+XHjfV8qrUqqedRt0weotvnKS7pcwK D2LQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726410698; x=1727015498; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=oI5g3/Lv8mNk/syaak4D+x2fQixDZyZK48NDDewIoU0=; b=cxvel2xpl7znZCdT7L0MO2lEhpa0koDl70jz0/Ut49fmT974qwWWnfa+92UgTRCrc6 e6piDxal3fEqGePkG9Qe30DLnMDKlH376eq19eTuVircsQe7PrXj9FnfmLYHRyuAOGdQ nt1QwFFkWRyfTXcq6NBWf7EmJxwrQw0DsPeNstwnf3zx5jCOjR9VPd8mhgrYammC84hq BAz2jy2cJIJccalwL7H5YGOn3hukVpMJu1vP0/pqplsLYP7TpliYXIsV5ik47CGDzHQH PN0D8qdnYPaFLBvKTAovEZl8GBXxXUBqBy+Tvy6vyfOzPFJlXOt+o6qHv2AlIlNLOqgI Gn7w== X-Forwarded-Encrypted: i=1; AJvYcCX2pbdWzWeA3ZOxBkXObY9zPRoGQaNmCWyJGgkFqHDafnXpZ8F/RMJZR49ekOHYUEoUonSLGtCv5uZuC01LWgQAZbch/sM=@vger.kernel.org X-Gm-Message-State: AOJu0YwrqGvk1pyFzZbPxHsddg4U4T95Olg0dXreZTOTY4wfwrzH9Es+ JwViRVpn04l6jpRzBABgZYP3aEFi+75+oeMDDCnBQv/wG2UoR6nEGmNqqfdOO2PZI7lWlN+q5Bc K5SsdRHaEKIVLZg== X-Google-Smtp-Source: AGHT+IHjXaRfgDfbsN9t5X94X1h1J0q/L8jWWMpEfes9EEkZxqwHEsm+kVBYzjFmCqJGj3geMIb4GpG0QYKCT58= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:35bd]) (user=aliceryhl job=sendgmr) by 2002:a05:690c:2030:b0:6db:c3b8:c4ce with SMTP id 00721157ae682-6dbc3b8c63fmr2582617b3.7.1726410698029; Sun, 15 Sep 2024 07:31:38 -0700 (PDT) Date: Sun, 15 Sep 2024 14:31:27 +0000 In-Reply-To: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=5617; i=aliceryhl@google.com; h=from:subject:message-id; bh=Oe7pz1TphdHtcU8pqH5nIlSad+Ilg1tAQMGPgpqw6+I=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBm5u/AAW7zNDH5xHrnIe2hBqOk4mGN9359+nykk RBk/acu8NKJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZubvwAAKCRAEWL7uWMY5 RrsvEACsWEL6NtArNiSxBd2J3ZysiPTJPROquuDhpEYvHs/lnaaixYq9Il7RoItxd+iZiX4Embv COeR6cFe2ERf4edkBwW7st7Ok5QG7VeeTTtfllDZsTR2GWhFqRhqhbg9OEuzhMJfRT1llD2+lQY cm+4uyMQwvBSvjyyNZT/90+hsfP2WFHOMN6LZbJf0BLX5w53VawcSXha/yeel5XTKJ3MH/S9srr xMul3q/SxCoKARSlkdR/sBa8mGlrJ034t29tNd5nR4Z9sk182TV32cIdMd9+b65R9tAr60GlPm5 NQ7K76du8S3nFLNeYNDiAK0YtzRmKY/HWAYex2+zykYTj7DhUxQ+DhaHWx1d/44TEMtfM8d3g37 yxkirA4RnADJcGD9UX2jk1f29yqXR7R6+Pkz3gfAv43RROqhnyaqTPTvwotviD3BV3/EWwULe8P g9w0amu8FU1d7G1j6bbM2fQ1DLs+MdZnn351dLjLSPIzj/60Qey4bQtM/j8mBIJR4EjdhcKA1fq yR5SwAMSuay0cAPCRbLS09w/IJo4vEOytcPYwKUrMOvncXDlP/Y1NAFjXGfHYDDkm9QuVpBlwJd iEtS97IQU4LqIR0NaKRRIYN6NdB+/d9GeGkqJeIP+4AXLHJ0owZUVexMOxGTZo5rrxSKgu/zYIN uJShrgMYV5BRRxA== X-Mailer: b4 0.13.0 Message-ID: <20240915-alice-file-v10-1-88484f7a3dcf@google.com> Subject: [PATCH v10 1/8] rust: types: add `NotThreadSafe` From: Alice Ryhl To: Paul Moore , James Morris , "Serge E. Hallyn" , Miguel Ojeda , Christian Brauner Cc: Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , Benno Lossin , Andreas Hindborg , Peter Zijlstra , Alexander Viro , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj=C3=B8?= =?utf-8?q?nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan , Dan Williams , Matthew Wilcox , Thomas Gleixner , Daniel Xu , Martin Rodriguez Reboredo , Trevor Gross , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org, Alice Ryhl , Kees Cook This introduces a new marker type for types that shouldn't be thread safe. By adding a field of this type to a struct, it becomes non-Send and non-Sync, which means that it cannot be accessed in any way from threads other than the one it was created on. This is useful for APIs that require globals such as `current` to remain constant while the value exists. We update two existing users in the Kernel to use this helper: * `Task::current()` - moving the return type of this value to a different thread would not be safe as you can no longer be guaranteed that the `current` pointer remains valid. * Lock guards. Mutexes and spinlocks should be unlocked on the same thread as where they were locked, so we enforce this using the Send trait. There are also additional users in later patches of this patchset. See [1] and [2] for the discussion that led to the introduction of this patch. Link: https://lore.kernel.org/all/nFDPJFnzE9Q5cqY7FwSMByRH2OAn_BpI4H53NQfWIlN6I2qfmAqnkp2wRqn0XjMO65OyZY4h6P4K2nAGKJpAOSzksYXaiAK_FoH_8QbgBI4=@proton.me/ [1] Link: https://lore.kernel.org/all/nFDPJFnzE9Q5cqY7FwSMByRH2OAn_BpI4H53NQfWIlN6I2qfmAqnkp2wRqn0XjMO65OyZY4h6P4K2nAGKJpAOSzksYXaiAK_FoH_8QbgBI4=@proton.me/ [2] Suggested-by: Benno Lossin Reviewed-by: Benno Lossin Reviewed-by: Trevor Gross Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Björn Roy Baron Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl --- rust/kernel/sync/lock.rs | 13 +++++++++---- rust/kernel/task.rs | 10 ++++++---- rust/kernel/types.rs | 21 +++++++++++++++++++++ 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs index f6c34ca4d819..d6e9bab114b8 100644 --- a/rust/kernel/sync/lock.rs +++ b/rust/kernel/sync/lock.rs @@ -6,8 +6,13 @@ //! spinlocks, raw spinlocks) to be provided with minimal effort. use super::LockClassKey; -use crate::{init::PinInit, pin_init, str::CStr, types::Opaque, types::ScopeGuard}; -use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned}; +use crate::{ + init::PinInit, + pin_init, + str::CStr, + types::{NotThreadSafe, Opaque, ScopeGuard}, +}; +use core::{cell::UnsafeCell, marker::PhantomPinned}; use macros::pin_data; pub mod mutex; @@ -139,7 +144,7 @@ pub fn lock(&self) -> Guard<'_, T, B> { pub struct Guard<'a, T: ?Sized, B: Backend> { pub(crate) lock: &'a Lock, pub(crate) state: B::GuardState, - _not_send: PhantomData<*mut ()>, + _not_send: NotThreadSafe, } // SAFETY: `Guard` is sync when the data protected by the lock is also sync. @@ -191,7 +196,7 @@ pub(crate) unsafe fn new(lock: &'a Lock, state: B::GuardState) -> Self { Self { lock, state, - _not_send: PhantomData, + _not_send: NotThreadSafe, } } } diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index 55dff7e088bf..278c623de0c6 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -4,10 +4,12 @@ //! //! C header: [`include/linux/sched.h`](srctree/include/linux/sched.h). -use crate::types::Opaque; +use crate::{ + bindings, + types::{NotThreadSafe, Opaque}, +}; use core::{ ffi::{c_int, c_long, c_uint}, - marker::PhantomData, ops::Deref, ptr, }; @@ -106,7 +108,7 @@ impl Task { pub unsafe fn current() -> impl Deref { struct TaskRef<'a> { task: &'a Task, - _not_send: PhantomData<*mut ()>, + _not_send: NotThreadSafe, } impl Deref for TaskRef<'_> { @@ -125,7 +127,7 @@ fn deref(&self) -> &Self::Target { // that `TaskRef` is not `Send`, we know it cannot be transferred to another thread // (where it could potentially outlive the caller). task: unsafe { &*ptr.cast() }, - _not_send: PhantomData, + _not_send: NotThreadSafe, } } diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 9e7ca066355c..3238ffaab031 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -532,3 +532,24 @@ unsafe impl AsBytes for str {} // does not have any uninitialized portions either. unsafe impl AsBytes for [T] {} unsafe impl AsBytes for [T; N] {} + +/// Zero-sized type to mark types not [`Send`]. +/// +/// Add this type as a field to your struct if your type should not be sent to a different task. +/// Since [`Send`] is an auto trait, adding a single field that is `!Send` will ensure that the +/// whole type is `!Send`. +/// +/// If a type is `!Send` it is impossible to give control over an instance of the type to another +/// task. This is useful to include in types that store or reference task-local information. A file +/// descriptor is an example of such task-local information. +/// +/// This type also makes the type `!Sync`, which prevents immutable access to the value from +/// several threads in parallel. +pub type NotThreadSafe = PhantomData<*mut ()>; + +/// Used to construct instances of type [`NotThreadSafe`] similar to how `PhantomData` is +/// constructed. +/// +/// [`NotThreadSafe`]: type@NotThreadSafe +#[allow(non_upper_case_globals)] +pub const NotThreadSafe: NotThreadSafe = PhantomData; From patchwork Sun Sep 15 14:31:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13804826 X-Patchwork-Delegate: paul@paul-moore.com Received: from mail-yw1-f201.google.com (mail-yw1-f201.google.com [209.85.128.201]) (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 BBB9F1C7B9C for ; Sun, 15 Sep 2024 14:31:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410703; cv=none; b=C2R5vUbaQFpCrlgwslca7Koozjb4oVDwHi2Gy+79EnVbHdzrvGFMjnNwH9HvuPsjsL2Mu+F9GUH2bbD9qnmOzo8XI5UoblvLUCAxioTMQh0z4woUA6bMTkiS0hFmDSqET8zsyorwREBNAG7us3jlGsM0Ws8jMw3u1REwpY+15mg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410703; c=relaxed/simple; bh=+rv6j785B2Asd3auywHXoPFhTSP9gSiszdqm+Xwr+aI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=XHnZwUMAWFnFtQC2tlYaHYfFv4S5pUUcUzYvEv5tUdfRCesftrra7vHgD2SJSQd4l22LBJ4CbY63PySrHK1lpEivXteqGOE/wSU9dcdcXyrRiE6m4VgmICFx56XCBQlks7SgXi3+F8n1nnklu8yoylpzZM+Trp+a/mwNV/vWmcE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Jb45IT8C; arc=none smtp.client-ip=209.85.128.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Jb45IT8C" Received: by mail-yw1-f201.google.com with SMTP id 00721157ae682-690404fd230so100912797b3.3 for ; Sun, 15 Sep 2024 07:31:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1726410701; x=1727015501; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=GkGmu1Ei8yfm/3eeEHU7BES63iCYbdgu3tOlhcgG6qc=; b=Jb45IT8CZx81WPkfxtHNPac4UK/a1cqOQfGL8i1cfYDnku8c0wa0tDgt85RsQbTBwy 7TIBbr8YAjOVQVI3/Jj6Q7ZPEOBTLLWZuRXhAPiQTbI0Zxub8c5xFyuYb7GFDtMXwDnr MDrm92MTEfIbLd6oOqcB0+s1bB49SjhxSgS7c/qQGJlEQGG39dPkOVFA+fUVNUeC6SPy jIzy+wSsZW8n9Yn0i6fk6x0TXvpjouOvE+P4Uoi+Dx332NxOdhX1RqNd+M+KOi5QyI9C sSs11zJcOaLdFmNlLQqbXPMLHlDGd5rR2sl/Dh+TZP6vLfjC7z9N4yccgz4Q+bsGR8QI S4Mg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726410701; x=1727015501; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=GkGmu1Ei8yfm/3eeEHU7BES63iCYbdgu3tOlhcgG6qc=; b=XqUGMmoUcVwN+IvAUeJMl7U10CDoy4xGV+h8tJN9NeWevV9D9ZQ5oF9uuTFBoNJsj7 NaKiJLtYPwvXvRqxGn8nEGBMO+F6bcp5gT10xQDY0XzZPbqYFLVllzTiHYujMoKaI8ra NBkw9d3XrzhndSkxHqwSFOLyDp4oH/GM1bXSkkcCAuQrUje4K33sbpfcmRnqfnNERqG1 RSE3zYM0cD2za95wiq5ht2nm1YFVPXgqcjE4P6BPokLbNVvqnfI7jp5gZiP/BmZ/cQ7K Yk7VY39Wt2cP1u0d/+2PAGArPjg4rtGtgiIW9rT7aoUgBcFy+vSGmMsKjbubEblmJWu1 dRUw== X-Forwarded-Encrypted: i=1; AJvYcCXgJ9QZbzKIVA+vnnZ7Dz8uWnS7E8ygbvNPAPChrz7+6pYkjWediVxo91+z+blbUFYb4Uopmx2EtPyehtjNyY4NE9x3pHA=@vger.kernel.org X-Gm-Message-State: AOJu0YykCgxZRoD6uFulmystz645ZRhSD9hjXkZdvYW3F4OB3LzgAx3V upm8/BnUNy+NFs+NCYWKS5R/rIzNDj8C7RjSZ//KJkowTNzaD1utkPvuI1b5cAXXhig3iQj1/Tq pxEoBmYMYUSQ+0w== X-Google-Smtp-Source: AGHT+IG4OTgHmcvBK8F9QzTwC03FC+JTINJZBl77CjBGWn4GOyO6KrG4A+heQeQ8GzPto43BxFkbBmHwFjwkG2U= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:35bd]) (user=aliceryhl job=sendgmr) by 2002:a05:690c:2a84:b0:6dc:97eb:ac51 with SMTP id 00721157ae682-6dc97ebb066mr126177b3.3.1726410700520; Sun, 15 Sep 2024 07:31:40 -0700 (PDT) Date: Sun, 15 Sep 2024 14:31:28 +0000 In-Reply-To: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=2424; i=aliceryhl@google.com; h=from:subject:message-id; bh=+rv6j785B2Asd3auywHXoPFhTSP9gSiszdqm+Xwr+aI=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBm5u/BRMuTCyk4C6ZuOxyOE1rsDuBB+ojWrrpQ7 PNWWdayP9iJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZubvwQAKCRAEWL7uWMY5 RvcUEACs+EHYmHyTXA5CqOjIpJvfFFGznoKIRV7P3FdTem4anQN4Ip2U7U3SZN3Seu9w4aN4VEf 9g73ONo8cEtyAa7AtlANEITVYf1+VtOkCqeqccGBlxyppfxFNCFTpIlfWdPMjgcvH/iDpDOeeeu Oz6HV80N4RS59/OODeu9KWfeFJIEoQTwwbagT+tUm2sYLr8gC094BqW6oY4Q9VHpiR9JzqQH2u8 YrHb4haDPSoeqTgjWGopNeXPzhssSkhxCA3u0aVlTPddRk9/gVXUv4pd12zaOyhPBfVRf+HzOeH m/7rczLTAgWjuD4EqgYErA18aJoP884SIEKcZgVFyqVth8qlvsx2dYGPAnhFPNH2GwgpXGNQmqb UjnmyLxWtSdO0v8yqs/g7hiCuE6uNVrxx2jFloTnzE/zLRICU/G2UlQXCG6aUBo6vuC3imFrbd2 XlajUVNWFG+L255yZF4Z5EYZgOP6VJm3ek63qWXGOjt+APpL6cqeGAKUJ2FSwjkhZb3PmYJ41kT qbcH+yWIIfjd3d7aaS4XNryMFCZ62ehb5zh3hid98wXk6DqSN0lowmhQYnqGYaQ7DQaGpUlsDZ8 BKzaSuu1qhgp2+OUlQJTiWw92LZm+rNv6xrJIvPnmI8R+lxbio5FS3pQypLJkIBjhAr3JiIcYK1 b71SAxy8IikXF+g== X-Mailer: b4 0.13.0 Message-ID: <20240915-alice-file-v10-2-88484f7a3dcf@google.com> Subject: [PATCH v10 2/8] rust: task: add `Task::current_raw` From: Alice Ryhl To: Paul Moore , James Morris , "Serge E. Hallyn" , Miguel Ojeda , Christian Brauner Cc: Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , Benno Lossin , Andreas Hindborg , Peter Zijlstra , Alexander Viro , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj=C3=B8?= =?utf-8?q?nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan , Dan Williams , Matthew Wilcox , Thomas Gleixner , Daniel Xu , Martin Rodriguez Reboredo , Trevor Gross , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org, Alice Ryhl , Kees Cook Introduces a safe function for getting a raw pointer to the current task. When writing bindings that need to access the current task, it is often more convenient to call a method that directly returns a raw pointer than to use the existing `Task::current` method. However, the only way to do that is `bindings::get_current()` which is unsafe since it calls into C. By introducing `Task::current_raw()`, it becomes possible to obtain a pointer to the current task without using unsafe. Link: https://lore.kernel.org/all/CAH5fLgjT48X-zYtidv31mox3C4_Ogoo_2cBOCmX0Ang3tAgGHA@mail.gmail.com/ Reviewed-by: Benno Lossin Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Trevor Gross Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl --- rust/kernel/task.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index 278c623de0c6..367b4bbddd9f 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -97,6 +97,15 @@ unsafe impl Sync for Task {} type Pid = bindings::pid_t; impl Task { + /// Returns a raw pointer to the current task. + /// + /// It is up to the user to use the pointer correctly. + #[inline] + pub fn current_raw() -> *mut bindings::task_struct { + // SAFETY: Getting the current pointer is always safe. + unsafe { bindings::get_current() } + } + /// Returns a task reference for the currently executing task/thread. /// /// The recommended way to get the current task/thread is to use the @@ -119,14 +128,12 @@ fn deref(&self) -> &Self::Target { } } - // SAFETY: Just an FFI call with no additional safety requirements. - let ptr = unsafe { bindings::get_current() }; - + let current = Task::current_raw(); TaskRef { // SAFETY: If the current thread is still running, the current task is valid. Given // that `TaskRef` is not `Send`, we know it cannot be transferred to another thread // (where it could potentially outlive the caller). - task: unsafe { &*ptr.cast() }, + task: unsafe { &*current.cast() }, _not_send: NotThreadSafe, } } From patchwork Sun Sep 15 14:31:29 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13804827 X-Patchwork-Delegate: paul@paul-moore.com Received: from mail-yw1-f201.google.com (mail-yw1-f201.google.com [209.85.128.201]) (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 735881C7B9E for ; Sun, 15 Sep 2024 14:31:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410706; cv=none; b=RPMgSIHx9MBD5purycXidLt4xhQLUuMvG9V7qDHqYBaDbElKgjdYsWTQmsH3h7V2EMgHWT+k+NceIVK9HDIw58D+D8FCf1ZO3u1wCBk4E15BO3Gni5TG7IAcwHgmt5G0wEJh9f3j7scCQVWLdnXQRuiD9DhKE4jhuWFetKoiRcQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410706; c=relaxed/simple; bh=/IHw/JxpuaCpt7rvyVYQv5wzSpojOc7cJSt8LNQAOOo=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=pYJHXPcpYHqRJrK4TR5+VyY7C9VuHIMhT+tIW6/MC3Ezb5rHzF6xfF54ZZC72QWYzkxEab4UB6S7vVVm4rBajqk18fWWvCNzHm0aA9Il7kp7ufkHwFN8iOqpBSnq2kUBmhKIk9BPDIcQbrrR5SKdT3GdTtnUV4yXZI76xrn2pLA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=CflzfH3a; arc=none smtp.client-ip=209.85.128.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="CflzfH3a" Received: by mail-yw1-f201.google.com with SMTP id 00721157ae682-6d7124939beso72123377b3.2 for ; Sun, 15 Sep 2024 07:31:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1726410703; x=1727015503; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=F6v1pXQKpbzspZFzdWxOapp3UYtVoUGluyOE80NlrTc=; b=CflzfH3am1fDUDGgvH2EOxVqFFhHaaAI6Miv8QO9qTnR2TOqKiTTUEbncxGjSSyKCI 6zj9wrpyxLjqfErNwsKDBZ8sEWtgmZwo3zVg7EDvbLhQFNpLsjDJdZ1XqSBalML8Q3UJ m3S5rWu9P4X0OJkrcj5vho6SUFNh9IwlGJe7OFmS0QSjrv3l7jI0Ktoz1z3SAsblbmGU te/97yCpETSC/Pg80chsSgL/vGtGRSj7YtppJfA4+yxSnNAdhDJhvtSOekdJSSCuhfS+ aBwKIqN0ofV2dJ9MJsrQ/0ku+IxDKVugcvpVwxaT1ZVpE61Q+zA8HVrS3+Y4XMzxuYGL 60dA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726410703; x=1727015503; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=F6v1pXQKpbzspZFzdWxOapp3UYtVoUGluyOE80NlrTc=; b=dOOxlCSdRdo+iYeXrNWr4wtw6VXccPNG3BDTfOpPAVhS/qOOmlvnDwuprS1w365EgA wVDrpHDi7UCZ7CeN//sw0ivgqnDdF+mbqrLBiQVO0jTNHJcviXsCeh3ZXbeOLklnTz/Y ICbykwVyXsdCJKW1nrmwJTH9E5whIXSrk4aSnQsdcpCdf/hdKUv7wC8J1aNu4zsx9BZN BLcIrf3tTAznPS1HYuawbxYA43ACcWEnRsaf3FY1hEtZoYR1rhsQuWTJHdYizlQaa0R7 L+CRqyZpCi20f25+wu8kcorwaDBd4eoQQTCbSmW2ezCxmHOA+ZscN0Isx9htc+LlTOD5 1asA== X-Forwarded-Encrypted: i=1; AJvYcCXc0m8+8NagdQtsKZDqxAWLoemf4ChtNaZcFFEMa6lJ2IB8tfwDvHLMBrW92FkPJ79WxXoHGwowvml2dQOpgXnBl4dIdIQ=@vger.kernel.org X-Gm-Message-State: AOJu0YxP1WNHYOGJ5q5jNgkUfGeAQDYzyG0XIOZL6LcIMHosGXdPv3P2 Ji0WkdUAwSZ5EaoH9pw8XAcq+622/dlgKxYz6rz3O8ATE7JspdzM8qXcGXXVCYc0wdcU6IhQbfz 86AhJCp2n2I1gdw== X-Google-Smtp-Source: AGHT+IFLKKFAqy0r+yKSEcuLg8J/5H4dH+VhKSO+92j+K0BwJtKOyu1l6wa+bAUa50GW8KpaR5GluAkLTxV1Z8w= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:35bd]) (user=aliceryhl job=sendgmr) by 2002:a81:b049:0:b0:68d:cbd6:e28 with SMTP id 00721157ae682-6dbcc579af6mr1538637b3.6.1726410703225; Sun, 15 Sep 2024 07:31:43 -0700 (PDT) Date: Sun, 15 Sep 2024 14:31:29 +0000 In-Reply-To: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=21544; i=aliceryhl@google.com; h=from:subject:message-id; bh=hZsOhDMMzvwVApbgdPUdjInL57NQ/zBfdGEUyBKb9sk=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBm5u/BWm3sQO3nuvNy3aTUjyDV+gv7CxcS3fm1a NVEIRnF3KOJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZubvwQAKCRAEWL7uWMY5 RrqAEACJjBzv9ivPYqqfzT7r02y9d4ARXK/5IuBYdcQlYNM5cgF3h7GmwRMi0W2+lSwMMIlKx38 JXgx6kEWXABByUwRlFxg0TZuPYRjzhk65BZov9Hj7hy40fyNzF5pTfaca4uhYEoW/cqZPgg8nMK b4facyjihZ/qxwIzuMtcRsN0ow6Xtt7vIoe6PSqT43ghUqdQP2mKuUQTZ4bNGciqSPvVaYMNjc3 Pt8ALsJLiCA3elGt+u7RIeGO1UA8nQPjQzhBPlLwMIroZ5fiLQYQmcERJhdQLxc3+96LAVlVWLz 5VtiNi9L+HoJtc7F0m9MTh1Rh1c74bSvtPmaO6MF1jUE2O6/01qa82u3Cl/h/gfTsBFzZhj8E85 t+gRVAwMIWqX7dGb153kDQ34MSyQq8OOV+YmxU8m1groOloMCj4wXPIRNl6a50Is6GuMEsy3gEh quBELJGFPUk52Vlyj9jJnsTtPHcbjsv6WQw/Hnfj9Ky/XOA2rpCUkVRSNlRqQw3K80iweCM9Nad PbpQMDNAMukCD0p8GLUZoUeGpOf583ZRgCkTJgL53YqCz0pILAtModd37k8b6PS/cGxYYJExyFL jKsQAt41SmlorVwvaLRDweEd7qfdZMXGTpFwztw0LPgenjjDY0L0Bo0+QYhQYHoqSXd2CJoZh5Q xyMstOhvn8/u+IQ== X-Mailer: b4 0.13.0 Message-ID: <20240915-alice-file-v10-3-88484f7a3dcf@google.com> Subject: [PATCH v10 3/8] rust: file: add Rust abstraction for `struct file` From: Alice Ryhl To: Paul Moore , James Morris , "Serge E. Hallyn" , Miguel Ojeda , Christian Brauner Cc: Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , Benno Lossin , Andreas Hindborg , Peter Zijlstra , Alexander Viro , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj=C3=B8?= =?utf-8?q?nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan , Dan Williams , Matthew Wilcox , Thomas Gleixner , Daniel Xu , Martin Rodriguez Reboredo , Trevor Gross , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org, Alice Ryhl , Kees Cook From: Wedson Almeida Filho This abstraction makes it possible to manipulate the open files for a process. The new `File` struct wraps the C `struct file`. When accessing it using the smart pointer `ARef`, the pointer will own a reference count to the file. When accessing it as `&File`, then the reference does not own a refcount, but the borrow checker will ensure that the reference count does not hit zero while the `&File` is live. Since this is intended to manipulate the open files of a process, we introduce an `fget` constructor that corresponds to the C `fget` method. In future patches, it will become possible to create a new fd in a process and bind it to a `File`. Rust Binder will use these to send fds from one process to another. We also provide a method for accessing the file's flags. Rust Binder will use this to access the flags of the Binder fd to check whether the non-blocking flag is set, which affects what the Binder ioctl does. This introduces a struct for the EBADF error type, rather than just using the Error type directly. This has two advantages: * `File::fget` returns a `Result, BadFdError>`, which the compiler will represent as a single pointer, with null being an error. This is possible because the compiler understands that `BadFdError` has only one possible value, and it also understands that the `ARef` smart pointer is guaranteed non-null. * Additionally, we promise to users of the method that the method can only fail with EBADF, which means that they can rely on this promise without having to inspect its implementation. That said, there are also two disadvantages: * Defining additional error types involves boilerplate. * The question mark operator will only utilize the `From` trait once, which prevents you from using the question mark operator on `BadFdError` in methods that return some third error type that the kernel `Error` is convertible into. (However, it works fine in methods that return `Error`.) Signed-off-by: Wedson Almeida Filho Co-developed-by: Daniel Xu Signed-off-by: Daniel Xu Co-developed-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Alice Ryhl Reviewed-by: Gary Guo --- fs/file.c | 7 + rust/bindings/bindings_helper.h | 2 + rust/helpers/fs.c | 12 ++ rust/helpers/helpers.c | 1 + rust/kernel/fs.rs | 8 + rust/kernel/fs/file.rs | 375 ++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 7 files changed, 406 insertions(+) diff --git a/fs/file.c b/fs/file.c index 655338effe9c..fc14209cf3e9 100644 --- a/fs/file.c +++ b/fs/file.c @@ -1123,6 +1123,13 @@ EXPORT_SYMBOL(task_lookup_next_fdget_rcu); * * The fput_needed flag returned by fget_light should be passed to the * corresponding fput_light. + * + * (As an exception to rule 2, you can call filp_close between fget_light and + * fput_light provided that you capture a real refcount with get_file before + * the call to filp_close, and ensure that this real refcount is fput *after* + * the fput_light call.) + * + * See also the documentation in rust/kernel/file.rs. */ static unsigned long __fget_light(unsigned int fd, fmode_t mask) { diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ae82e9c941af..4a400a954979 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -12,7 +12,9 @@ #include #include #include +#include #include +#include #include #include #include diff --git a/rust/helpers/fs.c b/rust/helpers/fs.c new file mode 100644 index 000000000000..a75c96763372 --- /dev/null +++ b/rust/helpers/fs.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2024 Google LLC. + */ + +#include + +struct file *rust_helper_get_file(struct file *f) +{ + return get_file(f); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 30f40149f3a9..3f2d0d0c8017 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -12,6 +12,7 @@ #include "build_assert.c" #include "build_bug.c" #include "err.c" +#include "fs.c" #include "kunit.c" #include "mutex.c" #include "page.c" diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs new file mode 100644 index 000000000000..0121b38c59e6 --- /dev/null +++ b/rust/kernel/fs.rs @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Kernel file systems. +//! +//! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) + +pub mod file; +pub use self::file::{File, LocalFile}; diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs new file mode 100644 index 000000000000..6adb7a7199ec --- /dev/null +++ b/rust/kernel/fs/file.rs @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Google LLC. + +//! Files and file descriptors. +//! +//! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) and +//! [`include/linux/file.h`](srctree/include/linux/file.h) + +use crate::{ + bindings, + error::{code::*, Error, Result}, + types::{ARef, AlwaysRefCounted, Opaque}, +}; +use core::ptr; + +/// Flags associated with a [`File`]. +pub mod flags { + /// File is opened in append mode. + pub const O_APPEND: u32 = bindings::O_APPEND; + + /// Signal-driven I/O is enabled. + pub const O_ASYNC: u32 = bindings::FASYNC; + + /// Close-on-exec flag is set. + pub const O_CLOEXEC: u32 = bindings::O_CLOEXEC; + + /// File was created if it didn't already exist. + pub const O_CREAT: u32 = bindings::O_CREAT; + + /// Direct I/O is enabled for this file. + pub const O_DIRECT: u32 = bindings::O_DIRECT; + + /// File must be a directory. + pub const O_DIRECTORY: u32 = bindings::O_DIRECTORY; + + /// Like [`O_SYNC`] except metadata is not synced. + pub const O_DSYNC: u32 = bindings::O_DSYNC; + + /// Ensure that this file is created with the `open(2)` call. + pub const O_EXCL: u32 = bindings::O_EXCL; + + /// Large file size enabled (`off64_t` over `off_t`). + pub const O_LARGEFILE: u32 = bindings::O_LARGEFILE; + + /// Do not update the file last access time. + pub const O_NOATIME: u32 = bindings::O_NOATIME; + + /// File should not be used as process's controlling terminal. + pub const O_NOCTTY: u32 = bindings::O_NOCTTY; + + /// If basename of path is a symbolic link, fail open. + pub const O_NOFOLLOW: u32 = bindings::O_NOFOLLOW; + + /// File is using nonblocking I/O. + pub const O_NONBLOCK: u32 = bindings::O_NONBLOCK; + + /// File is using nonblocking I/O. + /// + /// This is effectively the same flag as [`O_NONBLOCK`] on all architectures + /// except SPARC64. + pub const O_NDELAY: u32 = bindings::O_NDELAY; + + /// Used to obtain a path file descriptor. + pub const O_PATH: u32 = bindings::O_PATH; + + /// Write operations on this file will flush data and metadata. + pub const O_SYNC: u32 = bindings::O_SYNC; + + /// This file is an unnamed temporary regular file. + pub const O_TMPFILE: u32 = bindings::O_TMPFILE; + + /// File should be truncated to length 0. + pub const O_TRUNC: u32 = bindings::O_TRUNC; + + /// Bitmask for access mode flags. + /// + /// # Examples + /// + /// ``` + /// use kernel::fs::file; + /// # fn do_something() {} + /// # let flags = 0; + /// if (flags & file::flags::O_ACCMODE) == file::flags::O_RDONLY { + /// do_something(); + /// } + /// ``` + pub const O_ACCMODE: u32 = bindings::O_ACCMODE; + + /// File is read only. + pub const O_RDONLY: u32 = bindings::O_RDONLY; + + /// File is write only. + pub const O_WRONLY: u32 = bindings::O_WRONLY; + + /// File can be both read and written. + pub const O_RDWR: u32 = bindings::O_RDWR; +} + +/// Wraps the kernel's `struct file`. Thread safe. +/// +/// This represents an open file rather than a file on a filesystem. Processes generally reference +/// open files using file descriptors. However, file descriptors are not the same as files. A file +/// descriptor is just an integer that corresponds to a file, and a single file may be referenced +/// by multiple file descriptors. +/// +/// # Refcounting +/// +/// Instances of this type are reference-counted. The reference count is incremented by the +/// `fget`/`get_file` functions and decremented by `fput`. The Rust type `ARef` represents a +/// pointer that owns a reference count on the file. +/// +/// Whenever a process opens a file descriptor (fd), it stores a pointer to the file in its fd +/// table (`struct files_struct`). This pointer owns a reference count to the file, ensuring the +/// file isn't prematurely deleted while the file descriptor is open. In Rust terminology, the +/// pointers in `struct files_struct` are `ARef` pointers. +/// +/// ## Light refcounts +/// +/// Whenever a process has an fd to a file, it may use something called a "light refcount" as a +/// performance optimization. Light refcounts are acquired by calling `fdget` and released with +/// `fdput`. The idea behind light refcounts is that if the fd is not closed between the calls to +/// `fdget` and `fdput`, then the refcount cannot hit zero during that time, as the `struct +/// files_struct` holds a reference until the fd is closed. This means that it's safe to access the +/// file even if `fdget` does not increment the refcount. +/// +/// The requirement that the fd is not closed during a light refcount applies globally across all +/// threads - not just on the thread using the light refcount. For this reason, light refcounts are +/// only used when the `struct files_struct` is not shared with other threads, since this ensures +/// that other unrelated threads cannot suddenly start using the fd and close it. Therefore, +/// calling `fdget` on a shared `struct files_struct` creates a normal refcount instead of a light +/// refcount. +/// +/// Light reference counts must be released with `fdput` before the system call returns to +/// userspace. This means that if you wait until the current system call returns to userspace, then +/// all light refcounts that existed at the time have gone away. +/// +/// ### The file position +/// +/// Each `struct file` has a position integer, which is protected by the `f_pos_lock` mutex. +/// However, if the `struct file` is not shared, then the kernel may avoid taking the lock as a +/// performance optimization. +/// +/// The condition for avoiding the `f_pos_lock` mutex is different from the condition for using +/// `fdget`. With `fdget`, you may avoid incrementing the refcount as long as the current fd table +/// is not shared; it is okay if there are other fd tables that also reference the same `struct +/// file`. However, `fdget_pos` can only avoid taking the `f_pos_lock` if the entire `struct file` +/// is not shared, as different processes with an fd to the same `struct file` share the same +/// position. +/// +/// To represent files that are not thread safe due to this optimization, the [`LocalFile`] type is +/// used. +/// +/// ## Rust references +/// +/// The reference type `&File` is similar to light refcounts: +/// +/// * `&File` references don't own a reference count. They can only exist as long as the reference +/// count stays positive, and can only be created when there is some mechanism in place to ensure +/// this. +/// +/// * The Rust borrow-checker normally ensures this by enforcing that the `ARef` from which +/// a `&File` is created outlives the `&File`. +/// +/// * Using the unsafe [`File::from_raw_file`] means that it is up to the caller to ensure that the +/// `&File` only exists while the reference count is positive. +/// +/// * You can think of `fdget` as using an fd to look up an `ARef` in the `struct +/// files_struct` and create an `&File` from it. The "fd cannot be closed" rule is like the Rust +/// rule "the `ARef` must outlive the `&File`". +/// +/// # Invariants +/// +/// * All instances of this type are refcounted using the `f_count` field. +/// * There must not be any active calls to `fdget_pos` on this file that did not take the +/// `f_pos_lock` mutex. +#[repr(transparent)] +pub struct File { + inner: Opaque, +} + +// SAFETY: This file is known to not have any active `fdget_pos` calls that did not take the +// `f_pos_lock` mutex, so it is safe to transfer it between threads. +unsafe impl Send for File {} + +// SAFETY: This file is known to not have any active `fdget_pos` calls that did not take the +// `f_pos_lock` mutex, so it is safe to access its methods from several threads in parallel. +unsafe impl Sync for File {} + +// SAFETY: The type invariants guarantee that `File` is always ref-counted. This implementation +// makes `ARef` own a normal refcount. +unsafe impl AlwaysRefCounted for File { + #[inline] + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refcount is nonzero. + unsafe { bindings::get_file(self.as_ptr()) }; + } + + #[inline] + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: To call this method, the caller passes us ownership of a normal refcount, so we + // may drop it. The cast is okay since `File` has the same representation as `struct file`. + unsafe { bindings::fput(obj.cast().as_ptr()) } + } +} + +/// Wraps the kernel's `struct file`. Not thread safe. +/// +/// This type represents a file that is not known to be safe to transfer across thread boundaries. +/// To obtain a thread-safe [`File`], use the [`assume_no_fdget_pos`] conversion. +/// +/// See the documentation for [`File`] for more information. +/// +/// # Invariants +/// +/// * All instances of this type are refcounted using the `f_count` field. +/// * If there is an active call to `fdget_pos` that did not take the `f_pos_lock` mutex, then it +/// must be on the same thread as this file. +/// +/// [`assume_no_fdget_pos`]: LocalFile::assume_no_fdget_pos +pub struct LocalFile { + inner: Opaque, +} + +// SAFETY: The type invariants guarantee that `LocalFile` is always ref-counted. This implementation +// makes `ARef` own a normal refcount. +unsafe impl AlwaysRefCounted for LocalFile { + #[inline] + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refcount is nonzero. + unsafe { bindings::get_file(self.as_ptr()) }; + } + + #[inline] + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: To call this method, the caller passes us ownership of a normal refcount, so we + // may drop it. The cast is okay since `File` has the same representation as `struct file`. + unsafe { bindings::fput(obj.cast().as_ptr()) } + } +} + +impl LocalFile { + /// Constructs a new `struct file` wrapper from a file descriptor. + /// + /// The file descriptor belongs to the current process, and there might be active local calls + /// to `fdget_pos` on the same file. + /// + /// To obtain an `ARef`, use the [`assume_no_fdget_pos`] function to convert. + /// + /// [`assume_no_fdget_pos`]: LocalFile::assume_no_fdget_pos + #[inline] + pub fn fget(fd: u32) -> Result, BadFdError> { + // SAFETY: FFI call, there are no requirements on `fd`. + let ptr = ptr::NonNull::new(unsafe { bindings::fget(fd) }).ok_or(BadFdError)?; + + // SAFETY: `bindings::fget` created a refcount, and we pass ownership of it to the `ARef`. + // + // INVARIANT: This file is in the fd table on this thread, so either all `fdget_pos` calls + // are on this thread, or the file is shared, in which case `fdget_pos` calls took the + // `f_pos_lock` mutex. + Ok(unsafe { ARef::from_raw(ptr.cast()) }) + } + + /// Creates a reference to a [`LocalFile`] from a valid pointer. + /// + /// # Safety + /// + /// * The caller must ensure that `ptr` points at a valid file and that the file's refcount is + /// positive for the duration of 'a. + /// * The caller must ensure that if there is an active call to `fdget_pos` that did not take + /// the `f_pos_lock` mutex, then that call is on the current thread. + #[inline] + pub unsafe fn from_raw_file<'a>(ptr: *const bindings::file) -> &'a LocalFile { + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the + // duration of 'a. The cast is okay because `File` is `repr(transparent)`. + // + // INVARIANT: The caller guarantees that there are no problematic `fdget_pos` calls. + unsafe { &*ptr.cast() } + } + + /// Assume that there are no active `fdget_pos` calls that prevent us from sharing this file. + /// + /// This makes it safe to transfer this file to other threads. No checks are performed, and + /// using it incorrectly may lead to a data race on the file position if the file is shared + /// with another thread. + /// + /// This method is intended to be used together with [`LocalFile::fget`] when the caller knows + /// statically that there are no `fdget_pos` calls on the current thread. For example, you + /// might use it when calling `fget` from an ioctl, since ioctls usually do not touch the file + /// position. + /// + /// # Safety + /// + /// There must not be any active `fdget_pos` calls on the current thread. + #[inline] + pub unsafe fn assume_no_fdget_pos(me: ARef) -> ARef { + // INVARIANT: There are no `fdget_pos` calls on the current thread, and by the type + // invariants, if there is a `fdget_pos` call on another thread, then it took the + // `f_pos_lock` mutex. + // + // SAFETY: `LocalFile` and `File` have the same layout. + unsafe { ARef::from_raw(ARef::into_raw(me).cast()) } + } + + /// Returns a raw pointer to the inner C struct. + #[inline] + pub fn as_ptr(&self) -> *mut bindings::file { + self.inner.get() + } + + /// Returns the flags associated with the file. + /// + /// The flags are a combination of the constants in [`flags`]. + #[inline] + pub fn flags(&self) -> u32 { + // This `read_volatile` is intended to correspond to a READ_ONCE call. + // + // SAFETY: The file is valid because the shared reference guarantees a nonzero refcount. + // + // FIXME(read_once): Replace with `read_once` when available on the Rust side. + unsafe { core::ptr::addr_of!((*self.as_ptr()).f_flags).read_volatile() } + } +} + +impl File { + /// Creates a reference to a [`File`] from a valid pointer. + /// + /// # Safety + /// + /// * The caller must ensure that `ptr` points at a valid file and that the file's refcount is + /// positive for the duration of 'a. + /// * The caller must ensure that if there are active `fdget_pos` calls on this file, then they + /// took the `f_pos_lock` mutex. + #[inline] + pub unsafe fn from_raw_file<'a>(ptr: *const bindings::file) -> &'a File { + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the + // duration of 'a. The cast is okay because `File` is `repr(transparent)`. + // + // INVARIANT: The caller guarantees that there are no problematic `fdget_pos` calls. + unsafe { &*ptr.cast() } + } +} + +// Make LocalFile methods available on File. +impl core::ops::Deref for File { + type Target = LocalFile; + #[inline] + fn deref(&self) -> &LocalFile { + // SAFETY: The caller provides a `&File`, and since it is a reference, it must point at a + // valid file for the desired duration. + // + // By the type invariants, there are no `fdget_pos` calls that did not take the + // `f_pos_lock` mutex. + unsafe { LocalFile::from_raw_file(self as *const File as *const bindings::file) } + } +} + +/// Represents the `EBADF` error code. +/// +/// Used for methods that can only fail with `EBADF`. +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct BadFdError; + +impl From for Error { + #[inline] + fn from(_: BadFdError) -> Error { + EBADF + } +} + +impl core::fmt::Debug for BadFdError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.pad("EBADF") + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index f10b06a78b9d..c7d50f245f58 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -34,6 +34,7 @@ pub mod error; #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] pub mod firmware; +pub mod fs; pub mod init; pub mod ioctl; #[cfg(CONFIG_KUNIT)] From patchwork Sun Sep 15 14:31:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13804828 X-Patchwork-Delegate: paul@paul-moore.com Received: from mail-wm1-f73.google.com (mail-wm1-f73.google.com [209.85.128.73]) (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 7E2851C986F for ; Sun, 15 Sep 2024 14:31:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410709; cv=none; b=C5tcUMUQXYyXcNSBAW4U7u4sUpx/XhpVmD0bD+52Gfbj75n6xwTzYseKo8Os46ts5zKLohYkoO4g5WErdpVSbUwsZ0KwgJj96vv/NIr2fsUPg53b6QWsde6mNg4YBrx3RYdV+e7JPnQTrlDuYz6rfewZfSS50Q5rWCIB7WYaP4E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410709; c=relaxed/simple; bh=Wrg7hsJegJvAyHLxYLzqbt9d5c1eLvrv9c08VK8i6gI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=XRMJI/78AvtTgvwnneD4Zndl5lvbNKK9BpIOIMmtf9FIfzivHDEaTCge3zKQhj+Aa2vzh0YwcgKMFyaOjwNizAy4u1VMO3kgRckUbBaKW9MJYingw1kTKQWUpTrsD6FRRx7jgqXeTwCLLZnmiZmzzkubDsJODkxfZbm9OnZ4j9w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=C+VZ7ANl; arc=none smtp.client-ip=209.85.128.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="C+VZ7ANl" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-42cb471a230so27043395e9.3 for ; Sun, 15 Sep 2024 07:31:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1726410706; x=1727015506; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=wJrntt9EEx5ieWCRoCr13+FhRpz0IVeZBFKTzzkJrvk=; b=C+VZ7ANlvzs7dkaiS86g+z7aox85G2+ydbcHgJgkvMJ5WawfE+CJ3gfd9jXdej4x/l 9UD9lVfovNCTiKT5Q+d0lcoQ1w3bpwR0UsWZIdGiVMEuJm5mWaIsIo9sLVuLoVneCF/2 1e6QoS8qh6YnY3laY8XsNthMO8sBf6XQ/VRXPJfb86d3MXQvGiAUn1tWLfaKUd9GF+B5 pgNEW4bCOHOs16RLF2SnQlo+Qi4k/mwU6y8yA3wU/7XeA/CCparE82dW/fMY0ZGQoFRi 8GUCKPQHrRntPOuOVvaV9FWzU74/8ChC9CEz8aqjaUjV7sGBSl8vEUO/tjKDvck/EgUs lP4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726410706; x=1727015506; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=wJrntt9EEx5ieWCRoCr13+FhRpz0IVeZBFKTzzkJrvk=; b=Y9NRSv/sCvzD29e/jylOu4fHOGuVT7su2r72DH+JCBrwumtZrdaN01Dro3xUFfcowR 1mcQ++jZ7y2GO6YQ7MtUVqSVeaRCVE2qwiwax/xlMV91wJ5w3lHqUY+ZoYbSmigIlPxa +fCek8juOh7SqdMd6QcpcaS7aM3najrqZLkv473Eqz3WqU4dZwsaIWI2ib3AH2gMviER 1p5TFKDf7KGE2Ns0ywUS7c2d0jLK+XkzqX0W2SswFp8i2ZoyvJfdFbg50n1VMTsIsMKl wWSjEotsqzdXGy8ccbW02lcq3wiknXURG15dAk2el2zH8ahd8QJuKKhMBGBWRbFmPjH1 d7wg== X-Forwarded-Encrypted: i=1; AJvYcCV12fBIrrbN2mOATlrkIAGYgWwo2mMyRz2NAIEmhp6rCWtqE9FVsOuLS6QZTWfAzR86OGyIpZQ4CFLGwxKOUhiQ7kJcboU=@vger.kernel.org X-Gm-Message-State: AOJu0Yy0I0J/LP/BEEB10WDqpkKctF/oCpm+g+cY92g8VP+v4gOPJyoT 9X4WF187GpqnzvHdC/1YZUnWrFNZwZQ+7vFGReIQ4S2Y0eS5zJqMn4TnHYeaig10VyxZQCug/vw nuv6XC0YliOzWww== X-Google-Smtp-Source: AGHT+IG3R4ASaC0KijR1aECHjilHQ5gmuHF0HI7KyynXvt5LiFU0TMkiMOIJw+2yNuJQZRObBbKcsFiapFrTw+w= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:35bd]) (user=aliceryhl job=sendgmr) by 2002:a05:600c:1d28:b0:42c:b636:5891 with SMTP id 5b1f17b1804b1-42cdb569f96mr1813755e9.3.1726410705716; Sun, 15 Sep 2024 07:31:45 -0700 (PDT) Date: Sun, 15 Sep 2024 14:31:30 +0000 In-Reply-To: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=7648; i=aliceryhl@google.com; h=from:subject:message-id; bh=pZluqOYDewoyL/Pw33wzo0+9mXuz9XshwBqpSa+QO2E=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBm5u/CPUwRpX0HXjIIOfY6uZ0KzDr9daxhGzzRG bLR0Gy+y5yJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZubvwgAKCRAEWL7uWMY5 Rh6LEACEeOGnZkjswcGTbhfEd6qUCjTH4OdIecXnKr4sOCs7PU104TJpThMdfWV3TYbvI3IG0LL wTzbzGvY/O+my2pyHifEIwFfCgyvRQJYHm2lmFIQcKjgpX8sDzTn4/w9vdHj1+/PdrMKWN8U9rL kNjGyuCbMQaiJAT450K0+aUNS9IsBnfr59hLjWT39M5efj4tPqVrCHOHppf7YnRFsPF/A66tsOG aKiXmkyNzP3UB0hFZjO7olLiEFUGf83JMc1mE+GzQniYC3C2jdGli9WThGINMD22TwPMEMo7rJE dtDEnpEEOSJV+O97MwlEnk0bZ7OmE/9/fWFaLxCX5yBqtDVTLz5meC/y+ah5vpYGs7XPY40XTaF +4Q2TX0e2wf9GIIvw1ZAffnRrx6RbQUKGHdpOOTHvtaV84x1eDqCj3XaKCr5qbGgGvZCsuIqvc4 Lxnr+N2iSCIn+1KzrnBXD6VL3j1CZzBGOQDu0hWWEG2oaACYCFB1DdRU13GyK9fpv6Zlsj91okN 0Appvfc0WBft83SP3O6mwHQfoGqIQ1UPE7O30BC5Ujph75CZ+EioKc4DEKXlrqrgHyb1XYIvHJt XtFthh5wjYwUn23UfKJ/JcXEUI0RlGoRSk3vMNbGrl0IEGtQzJOdzhZGVoDR7qhcAHxwvh3RSXQ cXh9DO23N2Inxgw== X-Mailer: b4 0.13.0 Message-ID: <20240915-alice-file-v10-4-88484f7a3dcf@google.com> Subject: [PATCH v10 4/8] rust: cred: add Rust abstraction for `struct cred` From: Alice Ryhl To: Paul Moore , James Morris , "Serge E. Hallyn" , Miguel Ojeda , Christian Brauner Cc: Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , Benno Lossin , Andreas Hindborg , Peter Zijlstra , Alexander Viro , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj=C3=B8?= =?utf-8?q?nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan , Dan Williams , Matthew Wilcox , Thomas Gleixner , Daniel Xu , Martin Rodriguez Reboredo , Trevor Gross , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org, Alice Ryhl , Kees Cook From: Wedson Almeida Filho Add a wrapper around `struct cred` called `Credential`, and provide functionality to get the `Credential` associated with a `File`. Rust Binder must check the credentials of processes when they attempt to perform various operations, and these checks usually take a `&Credential` as parameter. The security_binder_set_context_mgr function would be one example. This patch is necessary to access these security_* methods from Rust. This Rust abstraction makes the following assumptions about the C side: * `struct cred` is refcounted with `get_cred`/`put_cred`. * It's okay to transfer a `struct cred` across threads, that is, you do not need to call `put_cred` on the same thread as where you called `get_cred`. * The `euid` field of a `struct cred` never changes after initialization. * The `f_cred` field of a `struct file` never changes after initialization. Signed-off-by: Wedson Almeida Filho Co-developed-by: Alice Ryhl Reviewed-by: Trevor Gross Reviewed-by: Benno Lossin Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Reviewed-by: Kees Cook Reviewed-by: Paul Moore --- rust/bindings/bindings_helper.h | 1 + rust/helpers/cred.c | 13 +++++++ rust/helpers/helpers.c | 1 + rust/kernel/cred.rs | 76 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/fs/file.rs | 13 +++++++ rust/kernel/lib.rs | 1 + 6 files changed, 105 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 4a400a954979..f74247205cb5 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/cred.c b/rust/helpers/cred.c new file mode 100644 index 000000000000..fde7ae20cdd1 --- /dev/null +++ b/rust/helpers/cred.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +const struct cred *rust_helper_get_cred(const struct cred *cred) +{ + return get_cred(cred); +} + +void rust_helper_put_cred(const struct cred *cred) +{ + put_cred(cred); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 3f2d0d0c8017..16e5de352dab 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -11,6 +11,7 @@ #include "bug.c" #include "build_assert.c" #include "build_bug.c" +#include "cred.c" #include "err.c" #include "fs.c" #include "kunit.c" diff --git a/rust/kernel/cred.rs b/rust/kernel/cred.rs new file mode 100644 index 000000000000..acee04768927 --- /dev/null +++ b/rust/kernel/cred.rs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Google LLC. + +//! Credentials management. +//! +//! C header: [`include/linux/cred.h`](srctree/include/linux/cred.h). +//! +//! Reference: + +use crate::{ + bindings, + types::{AlwaysRefCounted, Opaque}, +}; + +/// Wraps the kernel's `struct cred`. +/// +/// Credentials are used for various security checks in the kernel. +/// +/// Most fields of credentials are immutable. When things have their credentials changed, that +/// happens by replacing the credential instead of changing an existing credential. See the [kernel +/// documentation][ref] for more info on this. +/// +/// # Invariants +/// +/// Instances of this type are always ref-counted, that is, a call to `get_cred` ensures that the +/// allocation remains valid at least until the matching call to `put_cred`. +/// +/// [ref]: https://www.kernel.org/doc/html/latest/security/credentials.html +#[repr(transparent)] +pub struct Credential(Opaque); + +// SAFETY: +// - `Credential::dec_ref` can be called from any thread. +// - It is okay to send ownership of `Credential` across thread boundaries. +unsafe impl Send for Credential {} + +// SAFETY: It's OK to access `Credential` through shared references from other threads because +// we're either accessing properties that don't change or that are properly synchronised by C code. +unsafe impl Sync for Credential {} + +impl Credential { + /// Creates a reference to a [`Credential`] from a valid pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the + /// returned [`Credential`] reference. + pub unsafe fn from_ptr<'a>(ptr: *const bindings::cred) -> &'a Credential { + // SAFETY: The safety requirements guarantee the validity of the dereference, while the + // `Credential` type being transparent makes the cast ok. + unsafe { &*ptr.cast() } + } + + /// Returns the effective UID of the given credential. + pub fn euid(&self) -> bindings::kuid_t { + // SAFETY: By the type invariant, we know that `self.0` is valid. Furthermore, the `euid` + // field of a credential is never changed after initialization, so there is no potential + // for data races. + unsafe { (*self.0.get()).euid } + } +} + +// SAFETY: The type invariants guarantee that `Credential` is always ref-counted. +unsafe impl AlwaysRefCounted for Credential { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refcount is nonzero. + unsafe { bindings::get_cred(self.0.get()) }; + } + + unsafe fn dec_ref(obj: core::ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is nonzero. The cast is okay + // because `Credential` has the same representation as `struct cred`. + unsafe { bindings::put_cred(obj.cast().as_ptr()) }; + } +} diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs index 6adb7a7199ec..3c1f51719804 100644 --- a/rust/kernel/fs/file.rs +++ b/rust/kernel/fs/file.rs @@ -9,6 +9,7 @@ use crate::{ bindings, + cred::Credential, error::{code::*, Error, Result}, types::{ARef, AlwaysRefCounted, Opaque}, }; @@ -308,6 +309,18 @@ pub fn as_ptr(&self) -> *mut bindings::file { self.inner.get() } + /// Returns the credentials of the task that originally opened the file. + pub fn cred(&self) -> &Credential { + // SAFETY: It's okay to read the `f_cred` field without synchronization because `f_cred` is + // never changed after initialization of the file. + let ptr = unsafe { (*self.as_ptr()).f_cred }; + + // SAFETY: The signature of this function ensures that the caller will only access the + // returned credential while the file is still valid, and the C side ensures that the + // credential stays valid at least as long as the file. + unsafe { Credential::from_ptr(ptr) } + } + /// Returns the flags associated with the file. /// /// The flags are a combination of the constants in [`flags`]. diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index c7d50f245f58..c537d17c6db9 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -30,6 +30,7 @@ #[cfg(CONFIG_BLOCK)] pub mod block; mod build_assert; +pub mod cred; pub mod device; pub mod error; #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] From patchwork Sun Sep 15 14:31:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13804829 X-Patchwork-Delegate: paul@paul-moore.com Received: from mail-wm1-f74.google.com (mail-wm1-f74.google.com [209.85.128.74]) (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 C95561C9DC2 for ; Sun, 15 Sep 2024 14:31:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410711; cv=none; b=P4jCfK5E81fqbMVvtQL3eFFjNlkyYHv+O0e+8UzoowRgE8ovV6EJzDTEWa2JItxo+Lmo31W6zJvcrBINqQ6rcFS5pJBuxph9dqlYhjxoUWx4bm2e77RCzX/PzH3hpXi3U6Tazwcc+yBKD3OhaVAqYZO2EwU/xaMlAuRI1xFLNPs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410711; c=relaxed/simple; bh=rHxNMcsU4/HAgPUoaNjU7HprCPhh4gGEl0p1HneMhTE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ZND0DMvCMS9dHf7t+GmL9jADvhc+wdTOBr9nZq9lpF492l+GhwyXB8tqVZXrvSQ/Np8+2xziVxI/qdXocvzzqz4vxFUn+6eYXD7twAbKg2iLcoS3ccuuymU/Vu71qhY969kuj6IQBQnelaKRfo7X94RQJIywG5Pg5X4Bx7vnlFw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=lVko/vTO; arc=none smtp.client-ip=209.85.128.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="lVko/vTO" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-42cb89fbb8bso14376215e9.3 for ; Sun, 15 Sep 2024 07:31:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1726410708; x=1727015508; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=hhf+2TxlXGKWlx/uqFvTXCf9ALpmQAS6969H51nCFa0=; b=lVko/vTO5IM+Qg0qWwNdsl9gZAMY0JX3h0Bhn39CsC6UQvsVtrp3CfwL1aBMBTbJDT PII0oxzjSzOpbk1ri2n84+BqLZdMrlyUp9+UETEUc3Y5RsV1xUFa4Vz89rBi4dh9txsu YX/XZftpinbentQ+ZjfoYEUVj/uXBi6oAAa51MpjUyRcuce3LIk7wwZYIf9A4h+XPqnj FyLFqYDHvifXXJOgQ0VBVxgxirPmkawhIfsKiI+mXewZW10WWTI4uyJr4SUMko7zMyHu jQNB8e+9vki26NHk0FfxN5BYYDKH4x5a3l+VHqjPlor0jscQVHuJjuTsyUjhGXf8GeiE gtig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726410708; x=1727015508; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=hhf+2TxlXGKWlx/uqFvTXCf9ALpmQAS6969H51nCFa0=; b=YHd5kYczJLCU0Rtrhzh0WUFONF48no9rX6qHvhJ6hb4nTqe0Or1e/iNx1YkmuVGqS/ CJi/Zv8k/Tp+7nU4muq5D1rK9NaWACAWkSyNpqHjPe/zLBQEV0AyxfIVgPVTALZMMs66 kUIUFwBpEYIOsKhJqqsbqFw6jumawq74jDv7pMNoOEjlQpJ/wH3PKTBMa7Xd93/SeZ2r Kp50WvMPmLgvoW5O90DVbA4NepQMYhzPC9s6Cx7VEBHQuNgpEnEl3RVf6clhHDiy6sbF IKlKQENtmYxeyYDwaIZ8RWVVdSvO2tTRR6hCOcUsZxazB4BGxmf5pTyflKFtdI9GMPxT 5JdA== X-Forwarded-Encrypted: i=1; AJvYcCWsfqJbdI8aVPshg1mGiTaE2rneejKyE8+HilTOkAxTxw8dcS21oW4FEvdxeLX1OVLEU87uMe4tbyVXZ2RTVkujbJgMj5E=@vger.kernel.org X-Gm-Message-State: AOJu0YwfWXg+Q2aFalMc3RvB+zQR+FcuDS+vpjuzXTAIUmWw6VGopjKc j8jjAtEfbMHvyEpMKcIXzJh+WG11u/sS+Qe24jSfYG/AWFMzrQr1ITjYJHKgfbH7PLASaaf9jXy LuVqPgqJNwhQPNw== X-Google-Smtp-Source: AGHT+IEEd+a5D7B4kWGO4ZEoSBmt2tdUYh9kzQbla3X4XTKV4G6VeBfff1fcQYJOg7XO/a6pKFoBeXnQixg5njg= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:35bd]) (user=aliceryhl job=sendgmr) by 2002:a05:600c:4f89:b0:42c:a544:d10d with SMTP id 5b1f17b1804b1-42d964d91cemr143005e9.4.1726410708073; Sun, 15 Sep 2024 07:31:48 -0700 (PDT) Date: Sun, 15 Sep 2024 14:31:31 +0000 In-Reply-To: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=6847; i=aliceryhl@google.com; h=from:subject:message-id; bh=rHxNMcsU4/HAgPUoaNjU7HprCPhh4gGEl0p1HneMhTE=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBm5u/DDifR+ZXp/NjMDkFE8guCsChdl8NVHEyHR 8b0kanxH+SJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZubvwwAKCRAEWL7uWMY5 RmvwD/9O24XGdeoUeyyw0adR3X7srKi4qPSVniWVyGWtJzsz48wjAd6ZNqL6slvhUNNxhARj9nX Hhx06Uou+IPRM+vjHcBUPOpUC0KpDYJ6gy288lNylmUbCbGRYcgkap6zO47h7N+acd5ZdcUFDNS 7zfA8xJHLIRKgRLAceAVWuJ/EgnFPYTzPyZfXIqTUlKnHvrSzmXF9tZ3/qXmSKsJtaDEku3Hk3f 5xGh3SiByYAMrK/X8qZbqfBwXbRtoxYiIkRLu3w5kAngNtdlJ7wynJICCb4pxA3f5iw+2Cgbjl1 CWJKIpL9aNIxTDxYlSbKyMlWZgBVR/eJRP1iytVHyVDVyH7fxx6Id8wbTke+1JMXkcya0am97xk hw3/+hjIFQBeiy42o8Gyr/hM19SBW3iPSV+KFm9Cpv7lxk2kJ08a9BlC3cOdTzpm3avral4fVHD eF9/ZTPnYKJ2rkMfECpCepxiXFSPD604MbGKYaPXSc+4DO5sBFqjtj/NKanGRJu/3C4lDMnNiZ6 X9lHLTNZTMWdG/w0p71fi1O8aLnqvf/TFnqWzFqzD36sJOQ+jNdoU0iEw0uuGrvCP5pJlJUQewu Fgz+n6AUMQrzEhF8ccwyq8TWB/zfbNiUvu8NFt+NOQ9W9QShio60o4EyE/WvijfYnIRHv+4MDIJ sP+awf7h8FXj3uQ== X-Mailer: b4 0.13.0 Message-ID: <20240915-alice-file-v10-5-88484f7a3dcf@google.com> Subject: [PATCH v10 5/8] rust: security: add abstraction for secctx From: Alice Ryhl To: Paul Moore , James Morris , "Serge E. Hallyn" , Miguel Ojeda , Christian Brauner Cc: Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , Benno Lossin , Andreas Hindborg , Peter Zijlstra , Alexander Viro , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj=C3=B8?= =?utf-8?q?nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan , Dan Williams , Matthew Wilcox , Thomas Gleixner , Daniel Xu , Martin Rodriguez Reboredo , Trevor Gross , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org, Alice Ryhl , Kees Cook Add an abstraction for viewing the string representation of a security context. This is needed by Rust Binder because it has a feature where a process can view the string representation of the security context for incoming transactions. The process can use that to authenticate incoming transactions, and since the feature is provided by the kernel, the process can trust that the security context is legitimate. This abstraction makes the following assumptions about the C side: * When a call to `security_secid_to_secctx` is successful, it returns a pointer and length. The pointer references a byte string and is valid for reading for that many bytes. * The string may be referenced until `security_release_secctx` is called. * If CONFIG_SECURITY is set, then the three methods mentioned in rust/helpers are available without a helper. (That is, they are not a #define or `static inline`.) Reviewed-by: Benno Lossin Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Trevor Gross Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Reviewed-by: Kees Cook Acked-by: Paul Moore --- rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/security.c | 20 +++++++++++ rust/kernel/cred.rs | 8 +++++ rust/kernel/lib.rs | 1 + rust/kernel/security.rs | 74 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 105 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index f74247205cb5..51ec78c355c0 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 16e5de352dab..62022b18caf5 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -19,6 +19,7 @@ #include "page.c" #include "rbtree.c" #include "refcount.c" +#include "security.c" #include "signal.c" #include "slab.c" #include "spinlock.c" diff --git a/rust/helpers/security.c b/rust/helpers/security.c new file mode 100644 index 000000000000..239e5b4745fe --- /dev/null +++ b/rust/helpers/security.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#ifndef CONFIG_SECURITY +void rust_helper_security_cred_getsecid(const struct cred *c, u32 *secid) +{ + security_cred_getsecid(c, secid); +} + +int rust_helper_security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) +{ + return security_secid_to_secctx(secid, secdata, seclen); +} + +void rust_helper_security_release_secctx(char *secdata, u32 seclen) +{ + security_release_secctx(secdata, seclen); +} +#endif diff --git a/rust/kernel/cred.rs b/rust/kernel/cred.rs index acee04768927..92659649e932 100644 --- a/rust/kernel/cred.rs +++ b/rust/kernel/cred.rs @@ -52,6 +52,14 @@ pub unsafe fn from_ptr<'a>(ptr: *const bindings::cred) -> &'a Credential { unsafe { &*ptr.cast() } } + /// Get the id for this security context. + pub fn get_secid(&self) -> u32 { + let mut secid = 0; + // SAFETY: The invariants of this type ensures that the pointer is valid. + unsafe { bindings::security_cred_getsecid(self.0.get(), &mut secid) }; + secid + } + /// Returns the effective UID of the given credential. pub fn euid(&self) -> bindings::kuid_t { // SAFETY: By the type invariant, we know that `self.0` is valid. Furthermore, the `euid` diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index c537d17c6db9..e088c94a5a14 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -47,6 +47,7 @@ pub mod prelude; pub mod print; pub mod rbtree; +pub mod security; mod static_assert; #[doc(hidden)] pub mod std_vendor; diff --git a/rust/kernel/security.rs b/rust/kernel/security.rs new file mode 100644 index 000000000000..2522868862a1 --- /dev/null +++ b/rust/kernel/security.rs @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Google LLC. + +//! Linux Security Modules (LSM). +//! +//! C header: [`include/linux/security.h`](srctree/include/linux/security.h). + +use crate::{ + bindings, + error::{to_result, Result}, +}; + +/// A security context string. +/// +/// # Invariants +/// +/// The `secdata` and `seclen` fields correspond to a valid security context as returned by a +/// successful call to `security_secid_to_secctx`, that has not yet been destroyed by calling +/// `security_release_secctx`. +pub struct SecurityCtx { + secdata: *mut core::ffi::c_char, + seclen: usize, +} + +impl SecurityCtx { + /// Get the security context given its id. + pub fn from_secid(secid: u32) -> Result { + let mut secdata = core::ptr::null_mut(); + let mut seclen = 0u32; + // SAFETY: Just a C FFI call. The pointers are valid for writes. + to_result(unsafe { bindings::security_secid_to_secctx(secid, &mut secdata, &mut seclen) })?; + + // INVARIANT: If the above call did not fail, then we have a valid security context. + Ok(Self { + secdata, + seclen: seclen as usize, + }) + } + + /// Returns whether the security context is empty. + pub fn is_empty(&self) -> bool { + self.seclen == 0 + } + + /// Returns the length of this security context. + pub fn len(&self) -> usize { + self.seclen + } + + /// Returns the bytes for this security context. + pub fn as_bytes(&self) -> &[u8] { + let ptr = self.secdata; + if ptr.is_null() { + debug_assert_eq!(self.seclen, 0); + // We can't pass a null pointer to `slice::from_raw_parts` even if the length is zero. + return &[]; + } + + // SAFETY: The call to `security_secid_to_secctx` guarantees that the pointer is valid for + // `seclen` bytes. Furthermore, if the length is zero, then we have ensured that the + // pointer is not null. + unsafe { core::slice::from_raw_parts(ptr.cast(), self.seclen) } + } +} + +impl Drop for SecurityCtx { + fn drop(&mut self) { + // SAFETY: By the invariant of `Self`, this frees a pointer that came from a successful + // call to `security_secid_to_secctx` and has not yet been destroyed by + // `security_release_secctx`. + unsafe { bindings::security_release_secctx(self.secdata, self.seclen as u32) }; + } +} From patchwork Sun Sep 15 14:31:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13804830 X-Patchwork-Delegate: paul@paul-moore.com Received: from mail-wm1-f74.google.com (mail-wm1-f74.google.com [209.85.128.74]) (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 3755A1C9DE3 for ; Sun, 15 Sep 2024 14:31:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410714; cv=none; b=d7wEMWPjER6u0eBivJlyTef+e6IaGpIC7kZ/VmWvBDrr5dbuDjw+ccGmYvqmlIl1esTHKW5gLdZ6/iUm9+BuhfX1UW52j5/xhud4eiCoQwp+MiDNnUPpr5Wo09e1D/5mEwP6RYTCHskjrRrZBDB/q3yu1WEeJrYeBX9npqO6jPI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410714; c=relaxed/simple; bh=zqB7PMivLvaNHUeozQVnq1eG1vfCAIQC9md71JxVrLs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=b8A3hiRlcs8ZfOV/4TU62jLsIqSQHY868UuZgHt1PHAK4Tkk0gvIF2xqezRwqUoKZKbVAbOY8CiLRJWvh9XkcDOl1OPPiI1MyYscBV17x6N0MUaUur9CCZJsVfLiAdPEY2PLBRI7Unc+qjbvs017MXPUKg6GyezhMWCiFcuiQWE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=lP8c0Ltx; arc=none smtp.client-ip=209.85.128.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="lP8c0Ltx" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-42cbadcbb6eso27054945e9.2 for ; Sun, 15 Sep 2024 07:31:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1726410710; x=1727015510; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=2+9+srUDgJEOquigCBUY1zqr31yRtNobeqSqwQ52i1M=; b=lP8c0LtxvyXX9FUYg1x52c5qBPk8ZegLMieOr54Qi10SK57ST3JMwY3y8vBp1T0ntU cKOtzCgfWcc1VwSwCQQ4mwAuCgGe3tN0N0TGw4whDN41g3qmAWXNmd3BNy5ujMw7FvOq ch7pa3gfNrKzhKAT27bsnsuo1HYABXuXu3kgrKmb+oOovD5gHFKXHNA1hjK5q2zecHjR DQXR66LeMFj0je2IMea3mHAuZNRGzNC4SUySWpjYvMNVhL5AjPScfO8PiZkjPXhQH5FM ZC+rOXIFqI2WWW4ZG8dQcKuRbYnyc0bSrEa24BiYYloMMPaIICqwRT+w0rZM6h0F6tBb cRGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726410710; x=1727015510; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=2+9+srUDgJEOquigCBUY1zqr31yRtNobeqSqwQ52i1M=; b=CuGXjqUv37HKAEfep4U9PYiyL5TB5M4pRXiD6JTHNgFCcc8JiTC8r2eepidyF7w6LL FN/d2FfVrLokKFZSplkKzV3MVyZvG72kjiMZZwc7nQoAGMCEnVsMGyAKnE68gEQEsLYn PACcZglAdhCcCCeTyWwhZvoTtSI8JOlXcr8elj2ZcA++k+YuMix8VzcyWquMCjp95e7R v1T9tGJVIdWfqFAjvGctnjoc3v4fWkOjmWe4Ngkw+EUKBZ360zyrYiFAeqWjhKdIPZmP t/6ChwfuwIJ/k2QCY4FCm1ILMrvE52Q4aoIp1nNikU/IRnuncGiKQFZIG3cW7XRihQvL rUvA== X-Forwarded-Encrypted: i=1; AJvYcCX+1S/GOGNP2q2C6wdM3uoGycbQyUgfcku+2/Mrkr9VoKw47eaes2oNFxInhfgp8kN6bIlptF5rja22LIjjZGQOthuU/eQ=@vger.kernel.org X-Gm-Message-State: AOJu0YwZRtK8e0WGhihXDoxekf5vYFEWk8yRLBr2ys7S0x4OZZZSojdH Tq2iitJsLS3FBtuB1+W1n6W9utme39LdGTUraXcgaO759BSuw365G5OZMnk0mlXPlcQ8nIgvwDA dHRjluCDdH4z6WA== X-Google-Smtp-Source: AGHT+IEa4LNRUZs7U02EZPuMJYs3657T2iAsYFHpFziLsdiq+5y8nCs9e0TpPTZRP6H3NZwQoX4+Ge4se9q+Y6I= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:35bd]) (user=aliceryhl job=sendgmr) by 2002:a05:600c:4984:b0:42c:acab:d090 with SMTP id 5b1f17b1804b1-42cdb51153emr1817035e9.2.1726410710593; Sun, 15 Sep 2024 07:31:50 -0700 (PDT) Date: Sun, 15 Sep 2024 14:31:32 +0000 In-Reply-To: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=5412; i=aliceryhl@google.com; h=from:subject:message-id; bh=UusuKczZgpbZafCOu2SHAqb0ksf74QfeTn7LXrO42A0=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBm5u/DJeMSoYje+0B76NnFNXI/hIns7KoesrsKS 9PwYauTUkWJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZubvwwAKCRAEWL7uWMY5 RquqD/9TQ3EwSzosmOZT9P7R4KT0im5i2FrNhk8HJ76gol9DIi9UU4ScEWMCM+NFetVWf4RehPK LcXPxfYku7nxRS1Q0CpAKD1WXgbsImT3eD7/3DJ4wwgkMl6PXD6FSuNRb9dRmjAmjxK1Js5gG6b eJJ/LvGdExSCRFQ7h520CpWsagx324C7ZyuSn/xdJfaFMxCjYkxiVneQerAgqjuY5UKrqPQ+aOd B5EG3Hl7plv925Q7Ws7zDyAnT19mFD10H7Mj36M0C/AXt5VQbC4WmrxFt5iz+UPZVn0mT4uHRWq 2v8/pgihao9PgKIKBSJ9VvV8wOuwIpmZcY71mfndZSL3/0AY0UEQXXCg5ql7czStjxlbmTheNL8 8EUZ6qE3ycprb2DIg7GoLhPHblvUkFtPlp4874CNBcGyLHdfsvfvgcVDfnwt1q19ELxRZmC2zDg XZl7eKvZyXfQIH12aPJFCPzwGKOGxgNAEiBHCLXhIRAWiW336iLK9FH2d2wlkWTMA19/9HVN5Vk yvRkcHXR7Ksgd9z6DSqR0E7qBPfqtVa0X8EAOj6wsIGVRvRQ53oc4ghtapB6icS2v8MegGPHHKh y0QxlWRmLhKktA4SiVunzSA4WtQLGeJ0EqcADNPHXTxjg97pRfKBJCk34qJTlh0wYtEp+yaLIj1 VFIJRgUPq/BkiiQ== X-Mailer: b4 0.13.0 Message-ID: <20240915-alice-file-v10-6-88484f7a3dcf@google.com> Subject: [PATCH v10 6/8] rust: file: add `FileDescriptorReservation` From: Alice Ryhl To: Paul Moore , James Morris , "Serge E. Hallyn" , Miguel Ojeda , Christian Brauner Cc: Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , Benno Lossin , Andreas Hindborg , Peter Zijlstra , Alexander Viro , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj=C3=B8?= =?utf-8?q?nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan , Dan Williams , Matthew Wilcox , Thomas Gleixner , Daniel Xu , Martin Rodriguez Reboredo , Trevor Gross , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org, Alice Ryhl , Kees Cook From: Wedson Almeida Filho Allow for the creation of a file descriptor in two steps: first, we reserve a slot for it, then we commit or drop the reservation. The first step may fail (e.g., the current process ran out of available slots), but commit and drop never fail (and are mutually exclusive). This is needed by Rust Binder when fds are sent from one process to another. It has to be a two-step process to properly handle the case where multiple fds are sent: The operation must fail or succeed atomically, which we achieve by first reserving the fds we need, and only installing the files once we have reserved enough fds to send the files. Fd reservations assume that the value of `current` does not change between the call to get_unused_fd_flags and the call to fd_install (or put_unused_fd). By not implementing the Send trait, this abstraction ensures that the `FileDescriptorReservation` cannot be moved into a different process. Signed-off-by: Wedson Almeida Filho Co-developed-by: Alice Ryhl Reviewed-by: Benno Lossin Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Trevor Gross Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl --- rust/kernel/fs/file.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs index 3c1f51719804..e03dbe14d62a 100644 --- a/rust/kernel/fs/file.rs +++ b/rust/kernel/fs/file.rs @@ -11,7 +11,7 @@ bindings, cred::Credential, error::{code::*, Error, Result}, - types::{ARef, AlwaysRefCounted, Opaque}, + types::{ARef, AlwaysRefCounted, NotThreadSafe, Opaque}, }; use core::ptr; @@ -368,6 +368,79 @@ fn deref(&self) -> &LocalFile { } } +/// A file descriptor reservation. +/// +/// This allows the creation of a file descriptor in two steps: first, we reserve a slot for it, +/// then we commit or drop the reservation. The first step may fail (e.g., the current process ran +/// out of available slots), but commit and drop never fail (and are mutually exclusive). +/// +/// Dropping the reservation happens in the destructor of this type. +/// +/// # Invariants +/// +/// The fd stored in this struct must correspond to a reserved file descriptor of the current task. +pub struct FileDescriptorReservation { + fd: u32, + /// Prevent values of this type from being moved to a different task. + /// + /// The `fd_install` and `put_unused_fd` functions assume that the value of `current` is + /// unchanged since the call to `get_unused_fd_flags`. By adding this marker to this type, we + /// prevent it from being moved across task boundaries, which ensures that `current` does not + /// change while this value exists. + _not_send: NotThreadSafe, +} + +impl FileDescriptorReservation { + /// Creates a new file descriptor reservation. + pub fn get_unused_fd_flags(flags: u32) -> Result { + // SAFETY: FFI call, there are no safety requirements on `flags`. + let fd: i32 = unsafe { bindings::get_unused_fd_flags(flags) }; + if fd < 0 { + return Err(Error::from_errno(fd)); + } + Ok(Self { + fd: fd as u32, + _not_send: NotThreadSafe, + }) + } + + /// Returns the file descriptor number that was reserved. + pub fn reserved_fd(&self) -> u32 { + self.fd + } + + /// Commits the reservation. + /// + /// The previously reserved file descriptor is bound to `file`. This method consumes the + /// [`FileDescriptorReservation`], so it will not be usable after this call. + pub fn fd_install(self, file: ARef) { + // SAFETY: `self.fd` was previously returned by `get_unused_fd_flags`. We have not yet used + // the fd, so it is still valid, and `current` still refers to the same task, as this type + // cannot be moved across task boundaries. + // + // Furthermore, the file pointer is guaranteed to own a refcount by its type invariants, + // and we take ownership of that refcount by not running the destructor below. + // Additionally, the file is known to not have any non-shared `fdget_pos` calls, so even if + // this process starts using the file position, this will not result in a data race on the + // file position. + unsafe { bindings::fd_install(self.fd, file.as_ptr()) }; + + // `fd_install` consumes both the file descriptor and the file reference, so we cannot run + // the destructors. + core::mem::forget(self); + core::mem::forget(file); + } +} + +impl Drop for FileDescriptorReservation { + fn drop(&mut self) { + // SAFETY: By the type invariants of this type, `self.fd` was previously returned by + // `get_unused_fd_flags`. We have not yet used the fd, so it is still valid, and `current` + // still refers to the same task, as this type cannot be moved across task boundaries. + unsafe { bindings::put_unused_fd(self.fd) }; + } +} + /// Represents the `EBADF` error code. /// /// Used for methods that can only fail with `EBADF`. From patchwork Sun Sep 15 14:31:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13804831 X-Patchwork-Delegate: paul@paul-moore.com Received: from mail-wr1-f73.google.com (mail-wr1-f73.google.com [209.85.221.73]) (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 A4EEF1C9EAF for ; Sun, 15 Sep 2024 14:31:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410716; cv=none; b=Y4byt5WxFF1DIj00wYzka8+xHvomv/oOF5AaPiI22StDEmITN9L+tkmItvzZZkh0HT+n8EOcpIbBzQuYLJxke79YzitHFpAFuvVhyZFpX6pOpMHXQcvezdn74ZxY5hpuJuyJecr+ONwdm3Jxo2NB7Z9Z3AC9pRr080Ot8zOm/qU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410716; c=relaxed/simple; bh=XIbkrjlkZwB9EP6Wqa6JTDOm/egSkUncn5mejxybfIo=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=oMBsQth+yDMAn6KRO88mDwRsU8Mt5AEZY9XUuPxCqG6xICqDeTdBdWcrnxh9H5USrXBzgto4nTRD5ABs2YqAYS144scipIU3hHfQj99Rj7NPyj0BzuGQF5DHIBOy7VJAZYQR6t9q1I8bOvvL6z5LxawDEZjT0gJ2VXf0gHJpUjE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=xNyBKMnL; arc=none smtp.client-ip=209.85.221.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="xNyBKMnL" Received: by mail-wr1-f73.google.com with SMTP id ffacd0b85a97d-374b9a8512dso2035440f8f.2 for ; Sun, 15 Sep 2024 07:31:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1726410713; x=1727015513; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=x3SAZaQh7JMcKUvYE72Oqp+2UamZ4Jcb61cfVgkJJck=; b=xNyBKMnLhaD0xgCjU+jRq3XVRqkAxufvYq2+fllF/ODPHQE4UNUvbZgoU7/6LAE4uq Dqj4C1ZT/uW5dBlHbM+MKt51vsBbWRvTbylLXTBlpMc/MDkrv9IdjaWuADiqtNNAVAB0 kGzMLSY42jl9SjWl/c6duNcZW74gGv3BZj/CivvL/vs1tPh97oGse5aZtjU+SDya65SA jsD5GLE0/QAOK1bOAB1P7Z067MefUvegs+YuaQHxeEJfSY5ODeSiyRm00ps194dyeBcL PKBZ8V4He7Supmct2sMPV/o2PthJCu8CMIKQ4g1uAmaFWBNu+H5NmabukabPSy0aoMQq vaYw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726410713; x=1727015513; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=x3SAZaQh7JMcKUvYE72Oqp+2UamZ4Jcb61cfVgkJJck=; b=t49ZT46WD7gn5A0KfV/2IRESk0QagZfk3fc+5orJWas2g+TDLiylVH14EIEqgBIcac HC0zNMfyk5X/QWHsqP6d6EGWqzZ+5R5HJ78oNP1KOvIwgm78BEUJGgs3V0ZJ11YOWfGY gekDzKBAJmgz+kdpz/7KgFJ2VERkL+t/3LrltKj4QOPscrV0HuEX0lG0hwyxIXweb0WY EDTHNLGfaafnglvZ3PF9IMQF0swZ/DJ2vSofjpQrT+FfkdIl+K9JHXs65EpXF/l45ERe 49UWsh6mKeg/0DTLyaTJaGbm2HNZlVVxorzLCNGO79VwN0Iv6Mys2apErDVftdb1QnFT OyOQ== X-Forwarded-Encrypted: i=1; AJvYcCXiKJxVW60UYgdZDNgB24UkdFr5g4Ui6P5jkA98lM3MbfMWrRQGOVvovqmC+0/51ZJvAmKUCQV4th5hKd45UEnLuCEcf8M=@vger.kernel.org X-Gm-Message-State: AOJu0YwMjQa0GSabZ/T+Hyph6tPQXd0sNAzaWZ1NfKT5xuZTm2wBUwjz WkCNNX/KncDy0yKV7JnRVnzg3lnfPsy2GVpf/XOFVtxQM5RCDDdkmf4+E2xKobfwdfHQZ7AJC0S VFldrTbclVy7XZA== X-Google-Smtp-Source: AGHT+IGh2qvqT9yXKclHWWOXEWRXTNi/Ki/Hd1UaIOcNMCJ8X7DHDKNewGBgY5S0zaQfKvWokouq8hf51NPhJ9w= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:35bd]) (user=aliceryhl job=sendgmr) by 2002:adf:e891:0:b0:374:bd06:7ea3 with SMTP id ffacd0b85a97d-378c2cd0301mr14084f8f.2.1726410712932; Sun, 15 Sep 2024 07:31:52 -0700 (PDT) Date: Sun, 15 Sep 2024 14:31:33 +0000 In-Reply-To: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=7372; i=aliceryhl@google.com; h=from:subject:message-id; bh=XIbkrjlkZwB9EP6Wqa6JTDOm/egSkUncn5mejxybfIo=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBm5u/ELyHzU5PzBtNaIT472HnRdM+VdLVcDjxZI BuOFdbjtFCJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZubvxAAKCRAEWL7uWMY5 RhidD/0cdx3m5xZ/MXSpSebPSRSUseL7+ieVKT5MrkTThOpAeHW0pXj1HjzaF7gUY4oSjwXnSM1 CchwUx1AuBs90fPfU7dfStAt33OXkJgdQurxMy1zXo7+nCLqc3FNaZVvf/9GwxabggrZe8iNkrx EkGIKiofFO3/yAu8r+cDd4llboFf509NJoTHV6L9d8ZvPpEa6ak5TvVASPrduKAKksPuSgrDuee qkSECfi/vKPySJ7R6PYTzdcwnMfJjR9HAU5brqTHe7zML/hBUbVyAYo5T92pZd3I15gHcX3saYR 8Dw8v8NgZzGVZtz/hR4S4oRBgo04i7M4rjnjkYjGOQtS0Qo6UQdxGyEeKiI20Xb5h5N8OhnYo/D sfOlpVVlX7sQIjHkXHdNA+UPTFKAvpyFKUUgqJplOijU9D9GNMEdqpSucrhVoO9hTt2DWKXnaaX /bi4WxCxUAgfsB6ZXgh7N6x0qZXuj+6r2ujcbMgUW+88D/luhD5I2ILsG/wH3lmuwPUF4JqCmOK shKGtLSStu/G3SAJ+NHYaR+7sO/0P/nS84PQ9t0C7p+q+ubf9atzV9IX6P1mxbGDxF1lZV5zlgZ o2XR2oZeHgePzu/ZyYxFYSfY7iZyi6Y0ASYdnaDrCgW63f8EM6wxP+qYupbRblpJ+MSdFhupOky tXPqUpZz18tpVxA== X-Mailer: b4 0.13.0 Message-ID: <20240915-alice-file-v10-7-88484f7a3dcf@google.com> Subject: [PATCH v10 7/8] rust: file: add `Kuid` wrapper From: Alice Ryhl To: Paul Moore , James Morris , "Serge E. Hallyn" , Miguel Ojeda , Christian Brauner Cc: Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , Benno Lossin , Andreas Hindborg , Peter Zijlstra , Alexander Viro , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj=C3=B8?= =?utf-8?q?nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan , Dan Williams , Matthew Wilcox , Thomas Gleixner , Daniel Xu , Martin Rodriguez Reboredo , Trevor Gross , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org, Alice Ryhl , Kees Cook Adds a wrapper around `kuid_t` called `Kuid`. This allows us to define various operations on kuids such as equality and current_euid. It also lets us provide conversions from kuid into userspace values. Rust Binder needs these operations because it needs to compare kuids for equality, and it needs to tell userspace about the pid and uid of incoming transactions. To read kuids from a `struct task_struct`, you must currently use various #defines that perform the appropriate field access under an RCU read lock. Currently, we do not have a Rust wrapper for rcu_read_lock, which means that for this patch, there are two ways forward: 1. Inline the methods into Rust code, and use __rcu_read_lock directly rather than the rcu_read_lock wrapper. This gives up lockdep for these usages of RCU. 2. Wrap the various #defines in helpers and call the helpers from Rust. This patch uses the second option. One possible disadvantage of the second option is the possible introduction of speculation gadgets, but as discussed in [1], the risk appears to be acceptable. Of course, once a wrapper for rcu_read_lock is available, it is preferable to use that over either of the two above approaches. Link: https://lore.kernel.org/all/202312080947.674CD2DC7@keescook/ [1] Reviewed-by: Benno Lossin Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Trevor Gross Signed-off-by: Alice Ryhl --- rust/bindings/bindings_helper.h | 1 + rust/helpers/task.c | 38 ++++++++++++++++++++++++ rust/kernel/cred.rs | 5 ++-- rust/kernel/task.rs | 66 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 2 deletions(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 51ec78c355c0..e854ccddecee 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/task.c b/rust/helpers/task.c index 7ac789232d11..7d66487db831 100644 --- a/rust/helpers/task.c +++ b/rust/helpers/task.c @@ -17,3 +17,41 @@ void rust_helper_put_task_struct(struct task_struct *t) { put_task_struct(t); } + +kuid_t rust_helper_task_uid(struct task_struct *task) +{ + return task_uid(task); +} + +kuid_t rust_helper_task_euid(struct task_struct *task) +{ + return task_euid(task); +} + +#ifndef CONFIG_USER_NS +uid_t rust_helper_from_kuid(struct user_namespace *to, kuid_t uid) +{ + return from_kuid(to, uid); +} +#endif /* CONFIG_USER_NS */ + +bool rust_helper_uid_eq(kuid_t left, kuid_t right) +{ + return uid_eq(left, right); +} + +kuid_t rust_helper_current_euid(void) +{ + return current_euid(); +} + +struct user_namespace *rust_helper_current_user_ns(void) +{ + return current_user_ns(); +} + +pid_t rust_helper_task_tgid_nr_ns(struct task_struct *tsk, + struct pid_namespace *ns) +{ + return task_tgid_nr_ns(tsk, ns); +} diff --git a/rust/kernel/cred.rs b/rust/kernel/cred.rs index 92659649e932..81d67789b16f 100644 --- a/rust/kernel/cred.rs +++ b/rust/kernel/cred.rs @@ -10,6 +10,7 @@ use crate::{ bindings, + task::Kuid, types::{AlwaysRefCounted, Opaque}, }; @@ -61,11 +62,11 @@ pub fn get_secid(&self) -> u32 { } /// Returns the effective UID of the given credential. - pub fn euid(&self) -> bindings::kuid_t { + pub fn euid(&self) -> Kuid { // SAFETY: By the type invariant, we know that `self.0` is valid. Furthermore, the `euid` // field of a credential is never changed after initialization, so there is no potential // for data races. - unsafe { (*self.0.get()).euid } + Kuid::from_raw(unsafe { (*self.0.get()).euid }) } } diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index 367b4bbddd9f..1a36a9f19368 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -9,6 +9,7 @@ types::{NotThreadSafe, Opaque}, }; use core::{ + cmp::{Eq, PartialEq}, ffi::{c_int, c_long, c_uint}, ops::Deref, ptr, @@ -96,6 +97,12 @@ unsafe impl Sync for Task {} /// The type of process identifiers (PIDs). type Pid = bindings::pid_t; +/// The type of user identifiers (UIDs). +#[derive(Copy, Clone)] +pub struct Kuid { + kuid: bindings::kuid_t, +} + impl Task { /// Returns a raw pointer to the current task. /// @@ -157,12 +164,31 @@ pub fn pid(&self) -> Pid { unsafe { *ptr::addr_of!((*self.0.get()).pid) } } + /// Returns the UID of the given task. + pub fn uid(&self) -> Kuid { + // SAFETY: By the type invariant, we know that `self.0` is valid. + Kuid::from_raw(unsafe { bindings::task_uid(self.0.get()) }) + } + + /// Returns the effective UID of the given task. + pub fn euid(&self) -> Kuid { + // SAFETY: By the type invariant, we know that `self.0` is valid. + Kuid::from_raw(unsafe { bindings::task_euid(self.0.get()) }) + } + /// Determines whether the given task has pending signals. pub fn signal_pending(&self) -> bool { // SAFETY: By the type invariant, we know that `self.0` is valid. unsafe { bindings::signal_pending(self.0.get()) != 0 } } + /// Returns the given task's pid in the current pid namespace. + pub fn pid_in_current_ns(&self) -> Pid { + // SAFETY: We know that `self.0.get()` is valid by the type invariant, and passing a null + // pointer as the namespace is correct for using the current namespace. + unsafe { bindings::task_tgid_nr_ns(self.0.get(), ptr::null_mut()) } + } + /// Wakes up the task. pub fn wake_up(&self) { // SAFETY: By the type invariant, we know that `self.0.get()` is non-null and valid. @@ -184,3 +210,43 @@ unsafe fn dec_ref(obj: ptr::NonNull) { unsafe { bindings::put_task_struct(obj.cast().as_ptr()) } } } + +impl Kuid { + /// Get the current euid. + #[inline] + pub fn current_euid() -> Kuid { + // SAFETY: Just an FFI call. + Self::from_raw(unsafe { bindings::current_euid() }) + } + + /// Create a `Kuid` given the raw C type. + #[inline] + pub fn from_raw(kuid: bindings::kuid_t) -> Self { + Self { kuid } + } + + /// Turn this kuid into the raw C type. + #[inline] + pub fn into_raw(self) -> bindings::kuid_t { + self.kuid + } + + /// Converts this kernel UID into a userspace UID. + /// + /// Uses the namespace of the current task. + #[inline] + pub fn into_uid_in_current_ns(self) -> bindings::uid_t { + // SAFETY: Just an FFI call. + unsafe { bindings::from_kuid(bindings::current_user_ns(), self.kuid) } + } +} + +impl PartialEq for Kuid { + #[inline] + fn eq(&self, other: &Kuid) -> bool { + // SAFETY: Just an FFI call. + unsafe { bindings::uid_eq(self.kuid, other.kuid) } + } +} + +impl Eq for Kuid {} From patchwork Sun Sep 15 14:31:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13804832 X-Patchwork-Delegate: paul@paul-moore.com Received: from mail-yw1-f202.google.com (mail-yw1-f202.google.com [209.85.128.202]) (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 BF1F11C9EDB for ; Sun, 15 Sep 2024 14:31:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410718; cv=none; b=fHtbbAsbBwXC5OH+gWWY4o3w/VxbmVaiKzKAqELr6Bso6DJRGj1/FF88/hG5eSsqPBSkGGiYzx3U4cf0sw32Tw3QBFLgIi+RxuDV+5olCdfLIBLZg4gy65R8w2Uyo4lKioTgzuMRH1aL0Gm8hYewWCEn19zMdEW9ta3zOiR5CjY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726410718; c=relaxed/simple; bh=/bhxtkSWHddRRHFAN7ilQCw+DOk04+8fISK58//Qtwg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=uJRPzigkNcpPMlhr7bg7LDBjU0ve2DpX9j0xOYm0S3o0zInvxqs9Y6eorZBdhXZbQRCBe6AOvF0u3wnkebk6WqP2Lmfyq6JZcma4S2USrHDWfaa2/7Kc/LksNq6enOFhSsl/RMJLBfdnWuWQiZAnklaiI99UcHOw+MCRpA4UWIE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=vs1dnjdS; arc=none smtp.client-ip=209.85.128.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="vs1dnjdS" Received: by mail-yw1-f202.google.com with SMTP id 00721157ae682-69a0536b23aso110069017b3.3 for ; Sun, 15 Sep 2024 07:31:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1726410716; x=1727015516; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=di//Gho9LFvKMRY5a1n6309m9s0BrxpbnPkhPUuDJj8=; b=vs1dnjdSmq0U9sQ6kb4YzuTFcxbSec9IhoGTeHkroo3OSZfQPZ8Ln7ODiciz+o4ItP wWmnhXwifZdu5LOnQmuCVqQI5V2HIoNv5SLivWCcEfbBp7Gvul3VBTxK7qwSRs1wZzBk CBowbB/NLp+yjwBaKkJXZM2xBmrOiMMxDcVDcWtSdngnPs1+VPGkN19ReGSbOsdiDO+c vc9FfhoI8YQZBWN8Rcb4ekYRY0LiLIFVduz8wVMCglqPk52hxi0HHS391/g2vtkrujJh zeAZFpl6pEk8RJf/xatGEcGhGdpra35lalpkzIG2Qe2lRVtmQqLA1MdYHoNiwGDuQCRK jLIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726410716; x=1727015516; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=di//Gho9LFvKMRY5a1n6309m9s0BrxpbnPkhPUuDJj8=; b=O/nn1LJ2ToxYn+B3czu3X06CfsQV3VWP0LQJcTUxJ4Dc56qrPNB0dlIsGJ52/lLRZX PKPc0b08EtGIAzuFUeSpUf2YJ1HDa1QQhuZLSJpolJ8DBYNSYLgak0rvnSEEHZymZQL8 vZ5VHb4qSmk8QogmYmtWPoXiwOzbNyYfTEFi4sM1CWoW9O3sPf6Eta4ZD2pkx0Odjxid j3GUNUOdqBP3NkBY0Pu6gBmkh2bfh5beE9OGP4o18VkMEZTFLSBbgaLmKEYJPdDQlWmI F1bTMtLlIn/WO2etqqUAXr04vpc85++MGfBsRa+uZBhUgZwkN8joId0eHz9xvUf0xzh2 yGvg== X-Forwarded-Encrypted: i=1; AJvYcCUn5EyAhP/u56k5BAHZN60DArSJOLQqLwVftihExbi5mlfHxxVyN/pZj9ftcsCfrFhi+7CJOlVhdqPz163ayP1yE/KmNxE=@vger.kernel.org X-Gm-Message-State: AOJu0YzRPo3DJzx2lXDXzsMDqOaN5+UvGj/MDZYU9ZHLn/VrxCEf4AWj 0TzpNS+gYqazUaQazu3glHG1nB3b1NNnqDfuXIYPPNnzIGRcsb/mkTy42R7y9/mYBbMs+0/3dtN fJhKD73kYeZDMkg== X-Google-Smtp-Source: AGHT+IEqlJxhhgtl0WBF7hObPiWjQTUHQogWXSYtK0XmWW8HuhrSb0S1tCc/N2MSYjTsOMRd6dJrCxOBAsMkGxQ= X-Received: from aliceryhl.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:35bd]) (user=aliceryhl job=sendgmr) by 2002:a05:690c:201b:b0:650:a16c:91ac with SMTP id 00721157ae682-6dbb6bb973amr6339087b3.8.1726410715744; Sun, 15 Sep 2024 07:31:55 -0700 (PDT) Date: Sun, 15 Sep 2024 14:31:34 +0000 In-Reply-To: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20240915-alice-file-v10-0-88484f7a3dcf@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=7280; i=aliceryhl@google.com; h=from:subject:message-id; bh=/bhxtkSWHddRRHFAN7ilQCw+DOk04+8fISK58//Qtwg=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBm5u/FBSvbkR7slpTehWQOty+m7zbQ9LMW+MoUh 7o6gHEbB/mJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZubvxQAKCRAEWL7uWMY5 RkhLD/4i1Eo+PbWN3iXaWA3DOMC6LIYxC6mgd+Jv+qT/dG6AWMh684o0Slyi5rY0yygGtUuBjsK d62bBrWsXMZdAOZp1Qp0YodOcc1VbfqkgBWVLQemhfTvklq3TsA0mzEWtwqWzLt5o70FhnNCV15 NYEX0yKcNBw9Bj46xfWt/5ps7WdoTSKolRFu3c4pN1hUOMetj9DmK+B/pvwDXsrGJrsSPz+2uD2 /qe21/Ai3W05KpKThpgE1psdOk5bvRZLn4ITWOoE88P0Wo8i3wsHd6WH5nnzPOyJemlAl5K3uVT nUkPvHYsz9SgHJZ9+7JhUxfku7FXA3vta7UT0PcFeIjmH/vCDokvvGDZifY9teEvMru5ZGXMTHo ZTuNvovSSlJ4PdsGgK0Z+aa2p84X8cwr/RcN/WrmOZ18So1fUA7wGUS9Wyxsaeslwy8o2MsF5Dr 1cn6I3DKTHPB6ZO/Fe+3MWANfhl0xMxVzLBzih1ZQ+e49vhEW5AFfZYGAm8VnMSX1HdzbIweSEz h6h+MpbxuZYJJ7fKX85Kvi0qxoOfM4ilSGy1/zUaIiyMGe61vbv/JVvvtXydvDa566YcJcQv/7l SsVgKgS8gXR3KXExFknkGpsW+yfeG7m/W0q31Sif8fachxL4ryPBqIBoYVQnJ/zcLp7z+Pt/bxn wq/HzPM9DwjYDGQ== X-Mailer: b4 0.13.0 Message-ID: <20240915-alice-file-v10-8-88484f7a3dcf@google.com> Subject: [PATCH v10 8/8] rust: file: add abstraction for `poll_table` From: Alice Ryhl To: Paul Moore , James Morris , "Serge E. Hallyn" , Miguel Ojeda , Christian Brauner Cc: Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , Benno Lossin , Andreas Hindborg , Peter Zijlstra , Alexander Viro , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj=C3=B8?= =?utf-8?q?nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan , Dan Williams , Matthew Wilcox , Thomas Gleixner , Daniel Xu , Martin Rodriguez Reboredo , Trevor Gross , linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org, Alice Ryhl , Kees Cook The existing `CondVar` abstraction is a wrapper around `wait_queue_head`, but it does not support all use-cases of the C `wait_queue_head` type. To be specific, a `CondVar` cannot be registered with a `struct poll_table`. This limitation has the advantage that you do not need to call `synchronize_rcu` when destroying a `CondVar`. However, we need the ability to register a `poll_table` with a `wait_queue_head` in Rust Binder. To enable this, introduce a type called `PollCondVar`, which is like `CondVar` except that you can register a `poll_table`. We also introduce `PollTable`, which is a safe wrapper around `poll_table` that is intended to be used with `PollCondVar`. The destructor of `PollCondVar` unconditionally calls `synchronize_rcu` to ensure that the removal of epoll waiters has fully completed before the `wait_queue_head` is destroyed. That said, `synchronize_rcu` is rather expensive and is not needed in all cases: If we have never registered a `poll_table` with the `wait_queue_head`, then we don't need to call `synchronize_rcu`. (And this is a common case in Binder - not all processes use Binder with epoll.) The current implementation does not account for this, but if we find that it is necessary to improve this, a future patch could store a boolean next to the `wait_queue_head` to keep track of whether a `poll_table` has ever been registered. Reviewed-by: Benno Lossin Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Trevor Gross Signed-off-by: Alice Ryhl --- rust/bindings/bindings_helper.h | 1 + rust/kernel/sync.rs | 1 + rust/kernel/sync/poll.rs | 121 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index e854ccddecee..ca13659ded4c 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index 0ab20975a3b5..bae4a5179c72 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -11,6 +11,7 @@ mod condvar; pub mod lock; mod locked_by; +pub mod poll; pub use arc::{Arc, ArcBorrow, UniqueArc}; pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult}; diff --git a/rust/kernel/sync/poll.rs b/rust/kernel/sync/poll.rs new file mode 100644 index 000000000000..d5f17153b424 --- /dev/null +++ b/rust/kernel/sync/poll.rs @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Google LLC. + +//! Utilities for working with `struct poll_table`. + +use crate::{ + bindings, + fs::File, + prelude::*, + sync::{CondVar, LockClassKey}, + types::Opaque, +}; +use core::ops::Deref; + +/// Creates a [`PollCondVar`] initialiser with the given name and a newly-created lock class. +#[macro_export] +macro_rules! new_poll_condvar { + ($($name:literal)?) => { + $crate::sync::poll::PollCondVar::new( + $crate::optional_name!($($name)?), $crate::static_lock_class!() + ) + }; +} + +/// Wraps the kernel's `struct poll_table`. +/// +/// # Invariants +/// +/// This struct contains a valid `struct poll_table`. +/// +/// For a `struct poll_table` to be valid, its `_qproc` function must follow the safety +/// requirements of `_qproc` functions: +/// +/// * The `_qproc` function is given permission to enqueue a waiter to the provided `poll_table` +/// during the call. Once the waiter is removed and an rcu grace period has passed, it must no +/// longer access the `wait_queue_head`. +#[repr(transparent)] +pub struct PollTable(Opaque); + +impl PollTable { + /// Creates a reference to a [`PollTable`] from a valid pointer. + /// + /// # Safety + /// + /// The caller must ensure that for the duration of 'a, the pointer will point at a valid poll + /// table (as defined in the type invariants). + /// + /// The caller must also ensure that the `poll_table` is only accessed via the returned + /// reference for the duration of 'a. + pub unsafe fn from_ptr<'a>(ptr: *mut bindings::poll_table) -> &'a mut PollTable { + // SAFETY: The safety requirements guarantee the validity of the dereference, while the + // `PollTable` type being transparent makes the cast ok. + unsafe { &mut *ptr.cast() } + } + + fn get_qproc(&self) -> bindings::poll_queue_proc { + let ptr = self.0.get(); + // SAFETY: The `ptr` is valid because it originates from a reference, and the `_qproc` + // field is not modified concurrently with this call since we have an immutable reference. + unsafe { (*ptr)._qproc } + } + + /// Register this [`PollTable`] with the provided [`PollCondVar`], so that it can be notified + /// using the condition variable. + pub fn register_wait(&mut self, file: &File, cv: &PollCondVar) { + if let Some(qproc) = self.get_qproc() { + // SAFETY: The pointers to `file` and `self` need to be valid for the duration of this + // call to `qproc`, which they are because they are references. + // + // The `cv.wait_queue_head` pointer must be valid until an rcu grace period after the + // waiter is removed. The `PollCondVar` is pinned, so before `cv.wait_queue_head` can + // be destroyed, the destructor must run. That destructor first removes all waiters, + // and then waits for an rcu grace period. Therefore, `cv.wait_queue_head` is valid for + // long enough. + unsafe { qproc(file.as_ptr() as _, cv.wait_queue_head.get(), self.0.get()) }; + } + } +} + +/// A wrapper around [`CondVar`] that makes it usable with [`PollTable`]. +/// +/// [`CondVar`]: crate::sync::CondVar +#[pin_data(PinnedDrop)] +pub struct PollCondVar { + #[pin] + inner: CondVar, +} + +impl PollCondVar { + /// Constructs a new condvar initialiser. + pub fn new(name: &'static CStr, key: &'static LockClassKey) -> impl PinInit { + pin_init!(Self { + inner <- CondVar::new(name, key), + }) + } +} + +// Make the `CondVar` methods callable on `PollCondVar`. +impl Deref for PollCondVar { + type Target = CondVar; + + fn deref(&self) -> &CondVar { + &self.inner + } +} + +#[pinned_drop] +impl PinnedDrop for PollCondVar { + fn drop(self: Pin<&mut Self>) { + // Clear anything registered using `register_wait`. + // + // SAFETY: The pointer points at a valid `wait_queue_head`. + unsafe { bindings::__wake_up_pollfree(self.inner.wait_queue_head.get()) }; + + // Wait for epoll items to be properly removed. + // + // SAFETY: Just an FFI call. + unsafe { bindings::synchronize_rcu() }; + } +}