From patchwork Wed Nov 29 12:51:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13472810 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Wcql7DDk" Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EEC1A10F2 for ; Wed, 29 Nov 2023 04:51:30 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-da07b5e6f75so726271276.0 for ; Wed, 29 Nov 2023 04:51:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701262290; x=1701867090; 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=0wY83LCwqUfV5ZLdik3LbhCAbciT8Y3UyW5tGMN6GUE=; b=Wcql7DDkgMdcdjkmgGWgDrpdZeIw6QZLYIHx96qCZTQActcqYuuGBYFdRBeq/whyTi r79HNJSXDIp4LDAh0ngTOJNS5ygf6BUkirzPYbPOvtCsrXS/b59B62S/D/PqrDzajnch gnlhRgBvanZmsKta6rQEh0rFPF1HEEurswBafYg/UGkFSyfPtw4u36b4vfqulUjtW/aW B4YED2SvLROYBeO7j7bkmHR+BDqK+EjH4QeRrKVg9e8O+8+wITibLSciYy3iVVT4gSf5 NDSot6JAkMrzXGx65qo+/Z6QHm8Ct21HV/Zk51vcfHKdPERQSfoabwsCAkU92tO9zbQj zDOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701262290; x=1701867090; 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=0wY83LCwqUfV5ZLdik3LbhCAbciT8Y3UyW5tGMN6GUE=; b=lldUMq3Ghjfixr5xtE2qngGA4aw02cmUIzzDQJpb9CXwywNeuwdUqFQ0PPLlR197yQ eipn2G/F992j+Rldk1xwGxtCv60gUa1kAmqYP6LaNJN5gj92wNYQa0sSa0jaWg0u6T7Q pZE2pN2CwiANzWH1nD/NeUi16cJYApqvvMrchIXW4nUu/EeFTK5PXMIR2uEGS7xBth9E VtB3gJzZAcgTMwi6ygOWB3EsrlGA9Bu3vlE5qDgl4XQY/cC+q0jQIVYXYZsB7PtTQ0oi p+tAGz83jGWMPXWVjIB0GXpMEsJaLc/qWUUY42W7izSCkc5etDR7bMhInNfHreCE+zvz u6FQ== X-Gm-Message-State: AOJu0YzNkj6KOtn90NGFsl5Pdjh4+qjQsZ537k2chXKIuRg4VIVSAGVJ 3IMBF8WcsjOnO3VGvhURaAEbkmyWgKRW5mo= X-Google-Smtp-Source: AGHT+IG+UrMdVUxy6jruyHm5ZYnAeYayISK74kY3bjF/4Q8xH2zgmfstVYGpsFfZCZvasquhbqo2bhBcnaCfsIw= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a25:aa49:0:b0:d89:42d7:e72d with SMTP id s67-20020a25aa49000000b00d8942d7e72dmr690263ybi.3.1701262290111; Wed, 29 Nov 2023 04:51:30 -0800 (PST) Date: Wed, 29 Nov 2023 12:51:07 +0000 In-Reply-To: <20231129-alice-file-v1-0-f81afe8c7261@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231129-alice-file-v1-0-f81afe8c7261@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=10762; i=aliceryhl@google.com; h=from:subject:message-id; bh=Z4lADjotaTuRSVc0fj+8hkjF0ER41DwitA1BNgPnHsM=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlZzMxGPEnDhSL0gZ3T/QIiTXSSYiHUkIJ8K5UL 19ZFVCPb4uJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZWczMQAKCRAEWL7uWMY5 Rp5GD/9JDpxEHYcsLzT4LRf9umPIlT7KnoDaZw1OabNAZnDmAUszALaVDXOmD2h5vsrvYDk3+fm bUFM1IeOAHlF5yEEhT7K6XlyAkM9p1Y6kzHckQKg1IDj5eO3M6T9sqF62uTj5bezT1sDL5EXeh7 shpvppZ4NfeEpxX7eDgLkBvsFKDKBl4uHgOtNg4zbe324rYGch5dT4KeUJT3eQTQqQ0wS3DZJre M1FYPv9msoe+w0uCXNxy5x8J52BMPUcbpiCFfxYHZ15EAiTNy4uwJ1kPnH2SZUsowQWWBqRSOcf qX626iitC9uKIPZhd6hi9kFNgLH5H48RcuG5I+HGbb3b/Sy8QHdNChRK+jMAzVtfz760Iowi2Uk 1zgPgDnUXYrkNk8pOFBBeaQxYCcIXYgPY4XRCFFkG7H4pgIrLnfbUuhFx+WjuYPVk8Xc0gv4R4N PNTQcHJIDqrmqMBDxCIvpwcZ9hCtLa1uSv5gMxvOhqyPv2kggsFhFpT8NfBs4dECkHJn9LpCqdr njsdmAeSQjZJwspVc+P0idfRtiQfKxWWBqqT53EufW8QItIBqKxpWThM/ZUGfieJZC0D6cQAT0w ntLeafzej8pvAHIUlzOud0/aBU2taZQT66cDtQSKGp5tKosgT0I8ahmUyiOkZ4CZ2ffJ4Gp6MqF Ao6ZWllviVYQV/Q== X-Mailer: git-send-email 2.43.0.rc1.413.gea7ed67945-goog Message-ID: <20231129-alice-file-v1-1-f81afe8c7261@google.com> Subject: [PATCH 1/7] rust: file: add Rust abstraction for `struct file` From: Alice Ryhl To: Miguel Ojeda , 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 , Christian Brauner , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj?= =?utf-8?q?=C3=B8nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan Cc: Alice Ryhl , Dan Williams , Kees Cook , Matthew Wilcox , Thomas Gleixner , Daniel Xu , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org 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 a `from_fd` 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::from_fd` 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 Signed-off-by: Alice Ryhl --- rust/bindings/bindings_helper.h | 2 + rust/helpers.c | 7 ++ rust/kernel/file.rs | 182 ++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 4 files changed, 192 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 85f013ed4ca4..beed3ef1fbc3 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include #include diff --git a/rust/helpers.c b/rust/helpers.c index 70e59efd92bc..03141a3608a4 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -157,6 +158,12 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, } EXPORT_SYMBOL_GPL(rust_helper_init_work_with_key); +struct file *rust_helper_get_file(struct file *f) +{ + return get_file(f); +} +EXPORT_SYMBOL_GPL(rust_helper_get_file); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs new file mode 100644 index 000000000000..ee4ec8b919af --- /dev/null +++ b/rust/kernel/file.rs @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Files and file descriptors. +//! +//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h) and +//! [`include/linux/file.h`](../../../../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; + + /// Also known as `O_NDELAY`. + /// + /// 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::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`. +/// +/// # Invariants +/// +/// Instances of this type are always ref-counted, that is, a call to `get_file` ensures that the +/// allocation remains valid at least until the matching call to `fput`. +#[repr(transparent)] +pub struct File(Opaque); + +// SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. +// This means that the only situation in which a `File` can be accessed mutably is when the +// refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so +// it is ok for this type to be `Send`. +unsafe impl Send for File {} + +// SAFETY: It's OK to access `File` 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 File {} + +impl File { + /// Constructs a new `struct file` wrapper from a file descriptor. + /// + /// The file descriptor belongs to the current process. + pub fn from_fd(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)?; + + // INVARIANT: `fget` increments the refcount before returning. + Ok(unsafe { ARef::from_raw(ptr.cast()) }) + } + + /// Creates a reference to a [`File`] from a valid pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` points at a valid file and that its refcount does not + /// reach zero during the lifetime 'a. + pub unsafe fn from_ptr<'a>(ptr: *const bindings::file) -> &'a File { + // INVARIANT: The safety requirements guarantee that the refcount does not hit zero during + // 'a. The cast is okay because `File` is `repr(transparent)`. + unsafe { &*ptr.cast() } + } + + /// Returns the flags associated with the file. + /// + /// The flags are a combination of the constants in [`flags`]. + 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. + // + // TODO: Replace with `read_once` when available on the Rust side. + unsafe { core::ptr::addr_of!((*self.0.get()).f_flags).read_volatile() } + } +} + +// SAFETY: The type invariants guarantee that `File` is always ref-counted. +unsafe impl AlwaysRefCounted for File { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refcount is nonzero. + unsafe { bindings::get_file(self.0.get()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is nonzero. + unsafe { bindings::fput(obj.cast().as_ptr()) } + } +} + +/// Represents the `EBADF` error code. +/// +/// Used for methods that can only fail with `EBADF`. +pub struct BadFdError; + +impl From for Error { + 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 e6aff80b521f..ce9abceab784 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -34,6 +34,7 @@ mod allocator; mod build_assert; pub mod error; +pub mod file; pub mod init; pub mod ioctl; #[cfg(CONFIG_KUNIT)] From patchwork Wed Nov 29 12:51:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13472811 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="F2LYWsE3" Received: from mail-lj1-x249.google.com (mail-lj1-x249.google.com [IPv6:2a00:1450:4864:20::249]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 772E395 for ; Wed, 29 Nov 2023 04:51:34 -0800 (PST) Received: by mail-lj1-x249.google.com with SMTP id 38308e7fff4ca-2c83269c4ccso63577961fa.0 for ; Wed, 29 Nov 2023 04:51:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701262293; x=1701867093; 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=p6QB4S7RjpON9iiRgYxSOKOTgG1ip8XyxblOChu7yx8=; b=F2LYWsE3riR6r3HRnnpGZd2moP8fkxLa0AlW7T8OLYVDPnc7jjYAxB8fF+P2P4+MTz HNo6Y9Lnw72MEH1kuNjY0U5zNGgyb8ERKmoVbz8YwCmwT+NDM478XYo6A64ovgZ9Yo1X CWejhgLwy45kR1pkQYUmluWoi5I4sRzEE/urp58hy1ajF+TOgVIapnxY4PMTTq0NJ2j3 zIz4KvuUXXVO8pu6zSNBqCPcxivZNVoJGUPNnOkec4I7HzbvP8m8S8h0VL8++MsmKSyA taKQhb/3kxf8J2EWite6eVaD8KUVMe1ypvIkMlquvMeQCqplZEVbhdboFyO2eopK+KGV lwOw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701262293; x=1701867093; 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=p6QB4S7RjpON9iiRgYxSOKOTgG1ip8XyxblOChu7yx8=; b=c+Yn5xDk5vjmnf0VeL9wtT9JPrtd7j0Ii198QZ8AWuREWCN3wE6IzbDq/leBAo567e UD9dhWM2FqE+oizykDcZmKM6C37ON+WSvWV8j3cm9jX8j1wBD3HJwESeb4qTCvRLSWi+ 2m9wKn9o8f0Y02aGmdpOi2MOJ5jwfFl88pCxKwFrZgSG0M0zf7HsPVdhPGRJcDLLoh3H slkDp51XE4TpvMmnQsbp2g2YP22FTpfsFbG83JIedZN4P7Tpn5BRMt/Li4/49jPgwh3L SmCzVXyDlc86/DZXSxFeiKUqZ9gSqAShRvuY+f6Nr69xhbye+40WuLR+LUdO7x/md2mE p+QA== X-Gm-Message-State: AOJu0YyRKwTH8q48pczE3DNAjACOLUsQOuT5b1tZ4PctNn/+JciF0kBr eRBXLHYZTPSIKL/2Bqve9mY750tJkXHhXK0= X-Google-Smtp-Source: AGHT+IHS9AV6c+T1U8ggne7cDTOmDoVR3alPQBGekJkcrIMZ8l41L7D2dIg8QXwHUu1XELVu+8Dthc+cektvUCk= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a2e:b5a7:0:b0:2c8:82f1:d5b1 with SMTP id f7-20020a2eb5a7000000b002c882f1d5b1mr289874ljn.10.1701262292697; Wed, 29 Nov 2023 04:51:32 -0800 (PST) Date: Wed, 29 Nov 2023 12:51:08 +0000 In-Reply-To: <20231129-alice-file-v1-0-f81afe8c7261@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231129-alice-file-v1-0-f81afe8c7261@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=6667; i=aliceryhl@google.com; h=from:subject:message-id; bh=t47S0WZDVMIoa027rtsW157b15A9bzwhfrZLuzcINm0=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlZzMy43el8c2O94uLKsnTeu1ybBZWvATM7f0cf BISvhh54g2JAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZWczMgAKCRAEWL7uWMY5 RufXD/9m/h8mCcP5EbfN4dPSygW7RpJ+QIus6e6EEUo4+to0xhSzDPAN6BNxGP3RpOkle5lLSQH ae5cbBUluG5xEuvmoxRsW7UoaAXDUWQubZBLiuUlKzC+90UHknLFMyYpPNCLEZ/KAFeuMZ9/hSO 71ZhTJHZnge+O6bruAtFShQSQ5izfcldRce9WT7BDHamq54gf5arqb+d3IG5mQE6j5Nb3IargqR G2HMrlczL9/f5SXwLIsi3gGGC8U6EoPjBeoL/IX2SJ6oe9glgqc/8oKvrGdWNGc7of//TNAWyJU kjDZLiD2qymDZMW16Y9/gDvzA8nUYJ8yFucfDSGfl5J8CEbeG/t8MacJCEb4w3M23WzH5KzM/MX SxrXPUwWuuluYfvs76zIQp7VH6yg/zYpRSkcNHceWpDQBt5MneCv5lb/LDJyyUJj47jkC2Ey4Ya xBQIWr7RX7f63nU5D4ajNFQHvCVCpVSk/JJlMMGXR3/jocIUrbhW0eHraEX04qckruzE7wAwxaN kbiPyhThuCiyo9JtnKNoduMG5jyU87sIOM+b9Y7OWVOEeVKpDakGoPjP/84GvXIntuB759JPTxM /1wENIDAxy/KWM+31H70oQ94X6z9bRw7FWnBdevCF/ARG9Jk+KkvIck+nrSwYC1tUtBk9TeG824 sXUo65AMCEvaYHg== X-Mailer: git-send-email 2.43.0.rc1.413.gea7ed67945-goog Message-ID: <20231129-alice-file-v1-2-f81afe8c7261@google.com> Subject: [PATCH 2/7] rust: cred: add Rust abstraction for `struct cred` From: Alice Ryhl To: Miguel Ojeda , 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 , Christian Brauner , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj?= =?utf-8?q?=C3=B8nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan Cc: Alice Ryhl , Dan Williams , Kees Cook , Matthew Wilcox , Thomas Gleixner , Daniel Xu , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org 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. Signed-off-by: Wedson Almeida Filho Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl --- rust/bindings/bindings_helper.h | 1 + rust/helpers.c | 13 +++++++++ rust/kernel/cred.rs | 64 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/file.rs | 16 +++++++++++ rust/kernel/lib.rs | 1 + 5 files changed, 95 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index beed3ef1fbc3..6d1bd2229aab 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/rust/helpers.c b/rust/helpers.c index 03141a3608a4..10ed69f76424 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -164,6 +165,18 @@ struct file *rust_helper_get_file(struct file *f) } EXPORT_SYMBOL_GPL(rust_helper_get_file); +const struct cred *rust_helper_get_cred(const struct cred *cred) +{ + return get_cred(cred); +} +EXPORT_SYMBOL_GPL(rust_helper_get_cred); + +void rust_helper_put_cred(const struct cred *cred) +{ + put_cred(cred); +} +EXPORT_SYMBOL_GPL(rust_helper_put_cred); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/cred.rs b/rust/kernel/cred.rs new file mode 100644 index 000000000000..497058ec89bb --- /dev/null +++ b/rust/kernel/cred.rs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Credentials management. +//! +//! C header: [`include/linux/cred.h`](../../../../include/linux/cred.h) +//! +//! Reference: + +use crate::{ + bindings, + types::{AlwaysRefCounted, Opaque}, +}; + +/// Wraps the kernel's `struct cred`. +/// +/// # 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`. +#[repr(transparent)] +pub struct Credential(pub(crate) Opaque); + +// SAFETY: By design, the only way to access a `Credential` is via an immutable reference or an +// `ARef`. This means that the only situation in which a `Credential` can be accessed mutably is +// when the refcount drops to zero and the destructor runs. It is safe for that to happen on any +// thread, so it is ok for this type to be `Send`. +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. + 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. + unsafe { bindings::put_cred(obj.cast().as_ptr()) }; + } +} diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs index ee4ec8b919af..f1f71c3d97e2 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/file.rs @@ -7,6 +7,7 @@ use crate::{ bindings, + cred::Credential, error::{code::*, Error, Result}, types::{ARef, AlwaysRefCounted, Opaque}, }; @@ -138,6 +139,21 @@ pub unsafe fn from_ptr<'a>(ptr: *const bindings::file) -> &'a File { unsafe { &*ptr.cast() } } + /// Returns the credentials of the task that originally opened the file. + pub fn cred(&self) -> &Credential { + // 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. + // + // TODO: Replace with `read_once` when available on the Rust side. + let ptr = unsafe { core::ptr::addr_of!((*self.0.get()).f_cred).read_volatile() }; + + // SAFETY: The signature of this function ensures that the caller will only access the + // returned credential while the file is still valid, and the credential must stay valid + // while the file is valid. + 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 ce9abceab784..097fe9bb93ed 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -33,6 +33,7 @@ #[cfg(not(testlib))] mod allocator; mod build_assert; +pub mod cred; pub mod error; pub mod file; pub mod init; From patchwork Wed Nov 29 13:11:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13472843 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="jbrtbR+e" Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 15B55B2 for ; Wed, 29 Nov 2023 05:11:31 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-db402e6f61dso7808614276.3 for ; Wed, 29 Nov 2023 05:11:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701263490; x=1701868290; 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=b+PIqA5OWa+CUhuOr08fI/v9F95ZV2PMUhXtjoxfvkw=; b=jbrtbR+eeVkBLFt/BX0FL6SIkn4LGE/FDZkIJSwEH0SbY34WaGJ8BQK/MaWUqhJrFW QpFN2AljKfuPtdTIb9yNFPK8war4fcZVV272UY6BMKh4fr8RLn9JznEyccThFow5lJgs Z3l6aSu5iXFJOUPqCvT+uyplCOR4AWzIDkTSvbYpKVHLtPZi4advoPxPAp8MvgJ0hQvW 2bCfuI5csWgGqeZ/uaMBF+M7+ueO2ILP8TeDIgBlol+fySHsTXckNZ6s4wHKh3J3o6mf +Y7IUZ8RgBSrGsFOiU6esYlqjX4x+YRTSVV74xihLmE13lrvBX5wg4z1WA5mWtbS+zBs 3Npg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701263490; x=1701868290; 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=b+PIqA5OWa+CUhuOr08fI/v9F95ZV2PMUhXtjoxfvkw=; b=rlRGkil9n/hvMG0Z/S0QwfZzuLIElA0VRHVhd5demu+clHYhncZRBvvwujzxcWCM8u k+dgaDYupW98CPo5umy6klhqcETzbLwgjmEUXUdYRH4x9GAx9s7nBQU8ixJgYlnz6vco PpKSq2bco+SN8qhRBcwNAHzv7uupafpxNo/KefYGBguvfl1yzzQI9sDkMFMWm87AxAt0 TGlGHz1UL4fatHKCwylPr+RYyNbFFgysXg0PRZWIS4eJZ349//g3OfVDzi4y//hW/POL HFHlo81SoQ/DoPec7xfLr4Q9Vsts+dq5dnELfKkUpZmw0DIwPONK639xZdHcclnfHo6Z ZXOw== X-Gm-Message-State: AOJu0Ywb50T3cnQnL1cbOgCngdJvONxQS4xKSrSf6qiWtW7QVDYkpnWw TCtS5D/3TZMjTKdyldkfuqrd7+ZseJWOw8Y= X-Google-Smtp-Source: AGHT+IHmespvZy5s7Ip3mJ0mf6eFh6ZLQ6vzLqsvwESBu9GnbHUlPsKFocbK51mB9QzS1IwGPqksJwjBqcM/HnY= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a25:bac9:0:b0:daf:52c3:c373 with SMTP id a9-20020a25bac9000000b00daf52c3c373mr273071ybk.8.1701263490273; Wed, 29 Nov 2023 05:11:30 -0800 (PST) Date: Wed, 29 Nov 2023 13:11:25 +0000 In-Reply-To: <20231129-alice-file-v1-0-f81afe8c7261@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231129-alice-file-v1-0-f81afe8c7261@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=6420; i=aliceryhl@google.com; h=from:subject:message-id; bh=47u/r70ygVSpW6vTYUCJ4BVeloqGeXZGkJcYMBqQg5o=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlZzhqhiX8DHIjTD2wKQWwc5SHo00Hr117qRf8U TxwjCk011eJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZWc4agAKCRAEWL7uWMY5 RlqSD/9V5u/KKAdd4dXT2nyIcvtct0CAtU1kWe8krQxKvkXaq8ZGPyvco0tDjo+c5HOtgJdzHzi 9qi4W29Lt1TXWnku8QlgiagDyAnR5evvmSeU2/KS4kqWwJ6idD8HucS3wKMNrz8nwIo78z3rgYw iKlmXEBG7iu2HLzV7ydPqa6kxTeBtp35JZmxK+1ALPqdaqPDQeGeKFv7w4UZ4lrwN0sEO9A7JlK ugnfZy4Mq80QPK6EB33wbykZBTRZ+PVg6hu4FzF2QtcgO3UOgZ3vXG3dp3oVGoAeXEWqVfp+6g4 zbodTxkRiu3oG1RvhUq6kdMUi/xGV7auHqnPi+UdDEYkgZBCjZ3Y68PlpoH2Lxv6tQ1z/cvOLfH +cEW17uRBvtCPOPas1jWn3zQYlTeirdGoZ7OI9fmdUu+/Ml6rSyfHRYxWUtGUH4oHk+xK1gioV2 E0cEehl7bhy2UsgiTXobXdP13h+V/FoXriA0yhAbC3TOi0+kPmVRGQ2WvUJmAuVUgWQXKhbEjRN S6zpacIfQ+HZKP299C7x99V5gZytW/6SuwT0JQiYVM53oT50hj0bL9VbWQV7bX9rY0VuDzn/no/ qhJEBQ3JuvngATVmkctd1htWsNSZBdLOPPqKM7OIUNaBKWORXXEYnK5uqfC6jLyrYxFbA4I8XEb yEY0RLsm5tug3VA== X-Mailer: git-send-email 2.43.0.rc1.413.gea7ed67945-goog Message-ID: <20231129-alice-file-v1-3-f81afe8c7261@google.com> Subject: [PATCH 3/7] rust: security: add abstraction for secctx From: Alice Ryhl To: Miguel Ojeda , 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 , Christian Brauner , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj?= =?utf-8?q?=C3=B8nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan Cc: Alice Ryhl , Dan Williams , Kees Cook , Matthew Wilcox , Thomas Gleixner , Daniel Xu , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org Adds an abstraction for viewing the string representation of a security context. This is needed by Rust Binder because it has 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. Signed-off-by: Alice Ryhl --- rust/bindings/bindings_helper.h | 1 + rust/helpers.c | 21 +++++++++ rust/kernel/cred.rs | 8 ++++ rust/kernel/lib.rs | 1 + rust/kernel/security.rs | 78 +++++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+) create mode 100644 rust/kernel/security.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 6d1bd2229aab..81b13a953eae 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers.c b/rust/helpers.c index 10ed69f76424..fd633d9db79a 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -177,6 +178,26 @@ void rust_helper_put_cred(const struct cred *cred) } EXPORT_SYMBOL_GPL(rust_helper_put_cred); +#ifndef CONFIG_SECURITY +void rust_helper_security_cred_getsecid(const struct cred *c, u32 *secid) +{ + security_cred_getsecid(c, secid); +} +EXPORT_SYMBOL_GPL(rust_helper_security_cred_getsecid); + +int rust_helper_security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) +{ + return security_secid_to_secctx(secid, secdata, seclen); +} +EXPORT_SYMBOL_GPL(rust_helper_security_secid_to_secctx); + +void rust_helper_security_release_secctx(char *secdata, u32 seclen) +{ + security_release_secctx(secdata, seclen); +} +EXPORT_SYMBOL_GPL(rust_helper_security_release_secctx); +#endif + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/cred.rs b/rust/kernel/cred.rs index 497058ec89bb..3794937b5294 100644 --- a/rust/kernel/cred.rs +++ b/rust/kernel/cred.rs @@ -43,6 +43,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. diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 097fe9bb93ed..342cb02c495a 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -42,6 +42,7 @@ pub mod kunit; pub mod prelude; pub mod print; +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..69c10ed89a57 --- /dev/null +++ b/rust/kernel/security.rs @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Linux Security Modules (LSM). +//! +//! C header: [`include/linux/security.h`](../../../../include/linux/security.h). + +use crate::{ + bindings, + error::{to_result, Result}, +}; + +/// A security context string. +/// +/// The struct has the invariant that it always contains a valid security context. +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 = 0; + // SAFETY: Just a C FFI call. The pointers are valid for writes. + unsafe { + to_result(bindings::security_secid_to_secctx( + secid, + &mut secdata, + &mut seclen, + ))?; + } + + // If the above call did not fail, then we have a valid security + // context, so the invariants are not violated. + Ok(Self { + secdata, + seclen: usize::try_from(seclen).unwrap(), + }) + } + + /// 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 mut ptr = self.secdata; + if ptr.is_null() { + // Many C APIs will use null pointers for strings of length zero, but + // `slice::from_raw_parts` doesn't allow the pointer to be null even if the length is + // zero. Replace the pointer with a dangling but non-null pointer in this case. + debug_assert_eq!(self.seclen, 0); + ptr = core::ptr::NonNull::dangling().as_ptr(); + } + + // 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: This frees a pointer that came from a successful call to + // `security_secid_to_secctx`. + unsafe { + bindings::security_release_secctx(self.secdata, self.seclen as u32); + } + } +} From patchwork Wed Nov 29 13:11:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13472844 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="ZPSHsaSF" Received: from mail-lj1-x24a.google.com (mail-lj1-x24a.google.com [IPv6:2a00:1450:4864:20::24a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 647EE10E5 for ; Wed, 29 Nov 2023 05:12:06 -0800 (PST) Received: by mail-lj1-x24a.google.com with SMTP id 38308e7fff4ca-2c9bbb18743so9854011fa.1 for ; Wed, 29 Nov 2023 05:12:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701263524; x=1701868324; 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=esiXX39j7TBHYEJzpKp+/f8CbFmH8JGrCtrqMVnVTxs=; b=ZPSHsaSFsodDALg4wMXiPT1b9FBJ7F87h0+ORAPJ8RwYh6FjbWH8okFnbR+EMypaUp 0zik3t8ZadLYLgDxVhdcfyb9vQ+x5IDXVhiovvvgb10YljIJP0DTbVlNwLbjJIpBkmS7 zxCiIaj7dIjPSscCe9TKhBfGbj2PcQ72l+D2EABofmVVfbNMe1CCcsm5ws0/uEtGrsGn SuTOVxD2gDziukuxCxp8w9Y8BhM4HB/OSVtjW+vCC2a+L/eW2MhotwuXQ3kDgkIubeq/ lsSoPOapIA9kN2Heg8nPpt4evQ+j2OgyJiCMsIz27ihE7/6OEUYtmFyJimvNzA043+A1 UpjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701263524; x=1701868324; 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=esiXX39j7TBHYEJzpKp+/f8CbFmH8JGrCtrqMVnVTxs=; b=QoWKpf7QoTKhwAHrmzSZp6WIaAZS5blvR0+b/oMlfgBg2hjmbAMwFRpmAdxmZ22Jfh f8UVXwNff5nBIUKbCRvYMqPkKN4TA2Vi1DNtw3BHeDLDye7/LGrywS7rErgeDdO9oPY4 8B3xBdVqnPna6qOyvhlUah61jqVw7sU6aUAW6zsfQpSzh1giIxY6SI/QRSkxdSGmJdcr zE05eATLZrxzev2dwrJFliXAN/sPkVIc6YMG58eCPisyoGJRvsEiuryd2I3ZGYQDqGpJ y9FIjleI9ekFyG6lJJB7ash7gHtCpcyI/2ERfZ8Tt6bxvqUOruBUsor6E2vE9+e/ZiTz K0HA== X-Gm-Message-State: AOJu0YyjgMSZLnHqUDN7VobOZuNdNQqhOjTm0dzqSwzR+DrqDoJRQfhg BCU4Cufcyk6uwlVqTug0w3QLeZqbiyi1u/o= X-Google-Smtp-Source: AGHT+IEHkVVfWS9BaS8ByCS7vIZyPFgO7jKlTCeXF8NGMQTxeGrWD+UWcjF+D+ckG3L8k8lP8kqV0TrhZRqV9G0= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a2e:9110:0:b0:2c9:bc04:c096 with SMTP id m16-20020a2e9110000000b002c9bc04c096mr38076ljg.8.1701263524554; Wed, 29 Nov 2023 05:12:04 -0800 (PST) Date: Wed, 29 Nov 2023 13:11:56 +0000 In-Reply-To: <20231129-alice-file-v1-0-f81afe8c7261@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231129-alice-file-v1-0-f81afe8c7261@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=4281; i=aliceryhl@google.com; h=from:subject:message-id; bh=SUMv8jXGAij3l0jBYJ4Ms5z5jJZ9MMahswDWLeA0kW4=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlZzMyLtWU3gmtQGN7swi+G8KMYaE/xHeadwZsb f4gruq0HDuJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZWczMgAKCRAEWL7uWMY5 RljxD/45lWUS95ldd319LMIbnsKDH8muiHzIgmCnI/Ux8ekOfs63WXOW6PMtpqlv2iZobLGYJv0 P9w0M1AKHI9s8ubw8ZFE+DOe6aa/2VTShlRKLi+Ka60afXjsTAEOY7l7vlDeDkU2qc5yMDNqErg hykmgusYIDJpSoOc8OaPMuR+V8jwxue/cP233vUY1HLlTf0TD3j0wOXn7cd+7zqH9iitsRnNyY4 Osu/48J1Wgic6eeI4i6+z478nY0uXs1fAWFBz8veBAwl9LCzi4avMG4SG/9Jv/CVB6Beq21tH5P GezShTMCYkKx08ZCrK0ckmIlfVaoVcMNInULaCznCgTrOv1iIZS63z8hlYkIOQskVbG17LXo9nF RfjdNRMH2YwYuxW3wdkNXJBc1oEGQCqWF7vqYchI3ryaNgpdFUcMJxupAe046aPUyBBOLJPuVIJ 3r84UtEGw+76Z+VbNDnAQdyHNFhKpvAvN6iX2nh5Am6ytp3eqYCNQiQ1RvXKbrMqR0+n8JhJoEt cL7Sv4Ias/SNUymafiEKQymxb4Af3F8BgKMANSHTU3Zm+Frzxm44c4xBp3X01IaPO335vmvao+a TEKWUG+9kx2sOIv7JKHksvoWevZD+SHwwAGCtGE7CCtoOoFg5iWOY51/afby0TdZEsmJeidtTsP je6av9Munk+4B6w== X-Mailer: git-send-email 2.43.0.rc1.413.gea7ed67945-goog Message-ID: <20231129-alice-file-v1-4-f81afe8c7261@google.com> Subject: [PATCH 4/7] rust: file: add `FileDescriptorReservation` From: Alice Ryhl To: Miguel Ojeda , 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 , Christian Brauner , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj?= =?utf-8?q?=C3=B8nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan Cc: Alice Ryhl , Dan Williams , Kees Cook , Matthew Wilcox , Thomas Gleixner , Daniel Xu , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org 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 Signed-off-by: Alice Ryhl --- rust/kernel/file.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs index f1f71c3d97e2..2186a6ea3f2f 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/file.rs @@ -11,7 +11,7 @@ error::{code::*, Error, Result}, types::{ARef, AlwaysRefCounted, Opaque}, }; -use core::ptr; +use core::{marker::PhantomData, ptr}; /// Flags associated with a [`File`]. pub mod flags { @@ -180,6 +180,68 @@ unsafe fn dec_ref(obj: ptr::NonNull) { } } +/// 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. + /// + /// This is necessary because the C FFI calls assume that `current` is set to the task that + /// owns the fd in question. + _not_send_sync: PhantomData<*mut ()>, +} + +impl FileDescriptorReservation { + /// Creates a new file descriptor reservation. + pub fn new(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 _, + _not_send_sync: PhantomData, + }) + } + + /// 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 commit(self, file: ARef) { + // SAFETY: `self.fd` was previously returned by `get_unused_fd_flags`, and `file.ptr` is + // guaranteed to have an owned ref count by its type invariants. + unsafe { bindings::fd_install(self.fd, file.0.get()) }; + + // `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: `self.fd` was returned by a previous call to `get_unused_fd_flags`. + unsafe { bindings::put_unused_fd(self.fd) }; + } +} + /// Represents the `EBADF` error code. /// /// Used for methods that can only fail with `EBADF`. From patchwork Wed Nov 29 13:12:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13472845 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="seezu6Ah" Received: from mail-lf1-x14a.google.com (mail-lf1-x14a.google.com [IPv6:2a00:1450:4864:20::14a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0C68610DD for ; Wed, 29 Nov 2023 05:12:25 -0800 (PST) Received: by mail-lf1-x14a.google.com with SMTP id 2adb3069b0e04-50aa6b26836so7645899e87.3 for ; Wed, 29 Nov 2023 05:12:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701263543; x=1701868343; 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=Jdn+NMvalWu2DX8Rm0t7kAAGEBw7/t9Uqy6Z/eCT3dQ=; b=seezu6AhKOabbKhc6zu7EmPoVOghl6g8w6Jk2qMxHeSf7Ilowwzr0Iw+HNI2y7RwJD 8jvazds4UAVYd9mx7CTpb8EVlcCVEIBiP1wOPTffO12uhUJ8fTvuFi9X/AYT0i79EdwX 0UM2br3x645sI/mwtACih4p0BlgWtzY78zo2KVrPYgfQTv/nGyTM7cm6EUTxuMiaAF+Y fIAeFIVcaq5dQrixm6pNtMgqxGRDE7kRomc43h23cxf41o9PkdMJq+QmP2sT5L3DmAjA J8FmdZHQPsa3KPkzLrYs1pWTU/BTUVDt+JGtFugRGYqitGYAdhyIVdejxYRoLtCYXsav pGaw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701263543; x=1701868343; 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=Jdn+NMvalWu2DX8Rm0t7kAAGEBw7/t9Uqy6Z/eCT3dQ=; b=gq/g7ILtEqqi/bYgMTfw2HOek8RjSsPpo95ACVjasj/pLzeclj8ydGX8spGu9ksSso t1irrBASO6zV8DLCCSfy67AuoHsngfOJP1yXT4jOHlTJT8ExYhBkDi/tmJFNlrWNGRPl WStgf5/p1k+ud+XDDkdgDtkc2KksAmMN/1yfvNyLBhm1ZII+h35oiGQExvE2pwjKMuz0 MqdLm0JPmDNbIAwuBhUrugfhHmRYyKMnNUxMoNgByGXzqU5uMSuGKTsk8LhBKkfJJVKM V5OHFDYAyMomQWKW3mS3sfmiA8laWYB++dKeBZInfvvzQllmVnOnFlujDH9CJn1rfi8J ai/g== X-Gm-Message-State: AOJu0YywQ8QsC9rohv7kfSxWiSzdgzUaj1u9vb/v0vaxmSqSvARAo0l+ iahKEV+h+26+n2G391m8e/jLVRPlyH/H1eM= X-Google-Smtp-Source: AGHT+IEB/mXh/ZPS7PjkiGVqEG+SAnf3KNvYIg4NixJhPAVSZEHh3/5vKQR8huIBTAygzHua1slJ8dlnDO6e0dM= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:ac2:5224:0:b0:50b:bca7:968c with SMTP id i4-20020ac25224000000b0050bbca7968cmr58963lfl.11.1701263543178; Wed, 29 Nov 2023 05:12:23 -0800 (PST) Date: Wed, 29 Nov 2023 13:12:17 +0000 In-Reply-To: <20231129-alice-file-v1-0-f81afe8c7261@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231129-alice-file-v1-0-f81afe8c7261@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=6837; i=aliceryhl@google.com; h=from:subject:message-id; bh=fzhPWG3FyEb6bwd3huD/CCWPlg1T+DI/I6PnRrR6U60=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlZzMyxp/PiE2vc51gW7AeguEkf+zG+hlvclKDg z15KrtnzgeJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZWczMgAKCRAEWL7uWMY5 Rr0qD/sEfvqOb/IvSsLWcLMiUMGY1ZzjqsXSsbGWHIw5ZhJfuDcIjm4XM1OkrOwdyDRnUixIFHO rHrqNwf6vIUvWLx+d+0IKNUYFPRrx4Jo8T/9ITcfwCkYCj8WV1ouAXWe3EK154x+3SW6BkUj1W/ zsxlPzIy5zVju+h0FNheyCpN2Z8+XrvLR/q5ussUBYroyuT0AjGC0AeR4TA/pgcnQ60ek1YVniA 2Tqq3xzYdX7HlT9ivqOgzUCPWdfSaksZB6Zsj8VYS2QT1qiH19c9fu7razhL87/xeQbHBEwphHx aGghuDnMfE/oU/KlnMB2xHru4JP8E1NteTa4ZLMnLS/36cwT6sEELQq0mS1sJVqCpw0Fmrjx0qk AuKzgL5xhYqsroAD1/NX4GxAgaXrcIkRbdM84FY1ALLWZoWkYUydRGQRBxFybL+G/vr4IFK1bfP QXljESusHDDgQMCj4UPE0c+u9F8V3Wyhzy9iVbc+denXT/xR5nwg8EWMkugWZy7lPdGTyag1zFY MQguoJGaHrdpT9eAAf4fCtnjoTorm6MAOc7NOI2lfrHVIaK/l4aqmP8PX66//ke8l6eGZnuORaz zx5rCKJ5w8KFsKXp9EG9SkYJ+w9O4JDg9sn5ZtcOpKglyp/pR2fu2Iuw54AR4nClN4TAU5sm2D5 ouAI5yUjfk31JMQ== X-Mailer: git-send-email 2.43.0.rc1.413.gea7ed67945-goog Message-ID: <20231129-alice-file-v1-5-f81afe8c7261@google.com> Subject: [PATCH 5/7] rust: file: add `Kuid` wrapper From: Alice Ryhl To: Miguel Ojeda , 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 , Christian Brauner , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj?= =?utf-8?q?=C3=B8nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan Cc: Alice Ryhl , Dan Williams , Kees Cook , Matthew Wilcox , Thomas Gleixner , Daniel Xu , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org 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. Signed-off-by: Alice Ryhl --- rust/bindings/bindings_helper.h | 1 + rust/helpers.c | 45 ++++++++++++++++++++++++++ rust/kernel/cred.rs | 5 +-- rust/kernel/task.rs | 71 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 119 insertions(+), 3 deletions(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 81b13a953eae..700f01840188 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers.c b/rust/helpers.c index fd633d9db79a..58e3a9dff349 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -142,6 +142,51 @@ void rust_helper_put_task_struct(struct task_struct *t) } EXPORT_SYMBOL_GPL(rust_helper_put_task_struct); +kuid_t rust_helper_task_uid(struct task_struct *task) +{ + return task_uid(task); +} +EXPORT_SYMBOL_GPL(rust_helper_task_uid); + +kuid_t rust_helper_task_euid(struct task_struct *task) +{ + return task_euid(task); +} +EXPORT_SYMBOL_GPL(rust_helper_task_euid); + +#ifndef CONFIG_USER_NS +uid_t rust_helper_from_kuid(struct user_namespace *to, kuid_t uid) +{ + return from_kuid(to, uid); +} +EXPORT_SYMBOL_GPL(rust_helper_from_kuid); +#endif /* CONFIG_USER_NS */ + +bool rust_helper_uid_eq(kuid_t left, kuid_t right) +{ + return uid_eq(left, right); +} +EXPORT_SYMBOL_GPL(rust_helper_uid_eq); + +kuid_t rust_helper_current_euid(void) +{ + return current_euid(); +} +EXPORT_SYMBOL_GPL(rust_helper_current_euid); + +struct user_namespace *rust_helper_current_user_ns(void) +{ + return current_user_ns(); +} +EXPORT_SYMBOL_GPL(rust_helper_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); +} +EXPORT_SYMBOL_GPL(rust_helper_task_tgid_nr_ns); + struct kunit *rust_helper_kunit_get_current_test(void) { return kunit_get_current_test(); diff --git a/rust/kernel/cred.rs b/rust/kernel/cred.rs index 3794937b5294..fbc749788bfa 100644 --- a/rust/kernel/cred.rs +++ b/rust/kernel/cred.rs @@ -8,6 +8,7 @@ use crate::{ bindings, + task::Kuid, types::{AlwaysRefCounted, Opaque}, }; @@ -52,9 +53,9 @@ 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. - 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 b2299bc7ac1f..1a27b968a907 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -5,7 +5,12 @@ //! C header: [`include/linux/sched.h`](../../../../include/linux/sched.h). use crate::{bindings, types::Opaque}; -use core::{marker::PhantomData, ops::Deref, ptr}; +use core::{ + cmp::{Eq, PartialEq}, + marker::PhantomData, + ops::Deref, + ptr, +}; /// Returns the currently running task. #[macro_export] @@ -78,6 +83,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 task reference for the currently executing task/thread. /// @@ -132,12 +143,34 @@ 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. The rest is just FFI + // calls. + unsafe { + let namespace = bindings::task_active_pid_ns(bindings::get_current()); + bindings::task_tgid_nr_ns(self.0.get(), namespace) + } + } + /// 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. @@ -147,6 +180,42 @@ pub fn wake_up(&self) { } } +impl Kuid { + /// Get the current euid. + pub fn current_euid() -> Kuid { + // SAFETY: Just an FFI call. + Self { + kuid: unsafe { bindings::current_euid() }, + } + } + + /// Create a `Kuid` given the raw C type. + pub fn from_raw(kuid: bindings::kuid_t) -> Self { + Self { kuid } + } + + /// Turn this kuid into the raw C type. + pub fn into_raw(self) -> bindings::kuid_t { + self.kuid + } + + /// Converts this kernel UID into a UID that userspace understands. Uses the namespace of the + /// current task. + 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 { + fn eq(&self, other: &Kuid) -> bool { + // SAFETY: Just an FFI call. + unsafe { bindings::uid_eq(self.kuid, other.kuid) } + } +} + +impl Eq for Kuid {} + // SAFETY: The type invariants guarantee that `Task` is always ref-counted. unsafe impl crate::types::AlwaysRefCounted for Task { fn inc_ref(&self) { From patchwork Wed Nov 29 13:12:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13472846 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="oYuOv5Zj" Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4D43C10D0 for ; Wed, 29 Nov 2023 05:12:38 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5caf86963ecso99698827b3.3 for ; Wed, 29 Nov 2023 05:12:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701263557; x=1701868357; 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=cXjNNXUM6pnjCl+pZVw51JBLkEWhDl2ninKP9/3jCWk=; b=oYuOv5ZjXM2gp+/9ZyBoFqjtsRiuviJsdaBfN9uJXwB4StI7ZGoTrDcMDOn2xOgSgh HNsH4oc9LS0WsSXKknQhklnYgz2UZ88tdyOkfFScREW6TzUERdcrpcbFf3cV8ZY3HXhf 7CXUw2pahhGExQF6Hbt/m1Eq7O1F51bK/nv8H/0gxCyx3f/1kH2XHHxYvTpM0qQIRrF1 SZGGd4wr3n5yO7uNGPObDqo6xYZZ+qdgN147MQ+yk6XdVbbkUIxcpRxTTxvq/w5eI1Te wADhTTSIVRetrumwd2sczVTGMcyUyfUYw9caaTi3bEDAu6Zwu7NGZlZW0yNJHTimGC95 ixEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701263557; x=1701868357; 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=cXjNNXUM6pnjCl+pZVw51JBLkEWhDl2ninKP9/3jCWk=; b=VB07qLMWpHEuk1hOwZblBJivsFqeFtB6vSGOHxAA7sVeSy9qTI2jJQi1PkF2zHeF71 aiCM14StVRtJYlrrMR8ePjgWSEIT7mldAYjcpnqzgd+VGrOEpNEJJv56U1hLKpQEAbGg BvZ4joqJRLk3T5xbP7fgWW0NLHb8aIs8jpPEReduZ6jk+4mqtiqgd1dBbZXQ/3OlCebw HMcqv1sWev5HubGezkyC9yOICcqrfVpNYZOQNnV+3QCBjTT6M1gKZG5jX3MGkMMvWMZ7 sTUb02dbKULkbnly4a7U9PHh+/ZaP4ujdQVfqWSzNTP+Of5YBy9IJiABxj4PmcmZnwoS e+Vg== X-Gm-Message-State: AOJu0YxN5wjN65oQU5YN1FISv96mZgMlD4GMUa7NGm5fJmlXIFHgk4V4 zc+LhgkjUPO8+AicXjYcNzkfFQWWvCjiD6s= X-Google-Smtp-Source: AGHT+IGZ/DrVd6szNZuraGj6OJEwLI9AdSB0/YtOT+pXPvBEUA8z9WVNI5YOCH1nSunvGf16rHqPlCJprjA2dyY= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a05:690c:4283:b0:5ca:8688:a088 with SMTP id gj3-20020a05690c428300b005ca8688a088mr458584ywb.9.1701263557556; Wed, 29 Nov 2023 05:12:37 -0800 (PST) Date: Wed, 29 Nov 2023 13:12:32 +0000 In-Reply-To: <20231129-alice-file-v1-0-f81afe8c7261@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231129-alice-file-v1-0-f81afe8c7261@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=7762; i=aliceryhl@google.com; h=from:subject:message-id; bh=wm4v/CZhyQkEj/WgGp9wB8kmjgTXL9eNiFeuaaFW8cA=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlZzMyIu8t9+uRKkvsDVwPd7WtQJgdkH8MOJ0Kt /e6KpAU/aCJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZWczMgAKCRAEWL7uWMY5 Ro5vD/wLbe54Jjz/IxjFEJCY0ktIiNyNy2iSlK3mOLv/fhrdrJHbYqss5c6VmUWo2BVovvV9ZOi 02pYVCtMyM87CZg41oeeU13aB+Loz49/Ht3Adb9Vdj2vKcStC27h+CI+2V6bqPgle81kalHY2X8 qpxHvRaaweKd1sJ59O0fOo+UOvG+fOFnA3WaVNgsRAPgNChRKMsIkj/3GlHauCNLDmghNkJv/ho qjHocDdWkJc9tVY8c0/w3EYPBafgwJiyE4kkPqRlpe3m5jFh3k3lnxZwtERUU/OE79r4y9hdPaG b5PbLvscC5kvAkB3v4oLZnZOV9OrZ3Y1oKkJHWDaJ1XLe+JyD49E3SlH1yhSufvbK+GdG4aXrva SeWHVm5sC08bY0Cb8AsI65fKo9uepAkESCSnjI5y3LpUR3Jd9ENQ/h3O7bcCcnZZwfNX73E4q9G jUmnwetQEx78xh+PgST7eZv6RFEQazDEgYsrJrrgnPpXsquMvW8vNR+jPyXUoSh7c3V7dZ3JaQ3 Zd4wHG/Ic7Urzz6+C4BSwvNL4myhbdDOLqxqHigs/4CZQJiUMSA+6T+inDgXrjtdI3RVEGKEgEt R03VOXCt0B2c3mep5G0kHp4eOwVGUp/6jbU/428pk2pK/crQwzg8fmf6dZbIkEcAtRq2lL455N6 KOu7FHxEUjoKpcQ== X-Mailer: git-send-email 2.43.0.rc1.413.gea7ed67945-goog Message-ID: <20231129-alice-file-v1-6-f81afe8c7261@google.com> Subject: [PATCH 6/7] rust: file: add `DeferredFdCloser` From: Alice Ryhl To: Miguel Ojeda , 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 , Christian Brauner , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj?= =?utf-8?q?=C3=B8nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan Cc: Alice Ryhl , Dan Williams , Kees Cook , Matthew Wilcox , Thomas Gleixner , Daniel Xu , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org To close an fd from kernel space, we could call `ksys_close`. However, if we do this to an fd that is held using `fdget`, then we may trigger a use-after-free. Introduce a helper that can be used to close an fd even if the fd is currently held with `fdget`. This is done by grabbing an extra refcount to the file and dropping it in a task work once we return to userspace. This is necessary for Rust Binder because otherwise the user might try to have Binder close its fd for /dev/binder, which would cause problems as this happens inside an ioctl on /dev/binder, and ioctls hold the fd using `fdget`. Additional motivation can be found in commit 80cd795630d6 ("binder: fix use-after-free due to ksys_close() during fdget()") and in the comments on `binder_do_fd_close`. If there is some way to detect whether an fd is currently held with `fdget`, then this could be optimized to skip the allocation and task work when this is not the case. Another possible optimization would be to combine several fds into a single task work, since this is used with fd arrays that might hold several fds. That said, it might not be necessary to optimize it, because Rust Binder has two ways to send fds: BINDER_TYPE_FD and BINDER_TYPE_FDA. With BINDER_TYPE_FD, it is userspace's responsibility to close the fd, so this mechanism is used only by BINDER_TYPE_FDA, but fd arrays are used rarely these days. Signed-off-by: Alice Ryhl --- rust/bindings/bindings_helper.h | 2 + rust/helpers.c | 8 ++++ rust/kernel/file.rs | 84 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 700f01840188..c8daee341df6 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include /* `bindgen` gets confused at certain things. */ diff --git a/rust/helpers.c b/rust/helpers.c index 58e3a9dff349..d146bbf25aec 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -243,6 +244,13 @@ void rust_helper_security_release_secctx(char *secdata, u32 seclen) EXPORT_SYMBOL_GPL(rust_helper_security_release_secctx); #endif +void rust_helper_init_task_work(struct callback_head *twork, + task_work_func_t func) +{ + init_task_work(twork, func); +} +EXPORT_SYMBOL_GPL(rust_helper_init_task_work); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs index 2186a6ea3f2f..578ee307093f 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/file.rs @@ -11,7 +11,8 @@ error::{code::*, Error, Result}, types::{ARef, AlwaysRefCounted, Opaque}, }; -use core::{marker::PhantomData, ptr}; +use alloc::boxed::Box; +use core::{alloc::AllocError, marker::PhantomData, mem, ptr}; /// Flags associated with a [`File`]. pub mod flags { @@ -242,6 +243,87 @@ fn drop(&mut self) { } } +/// Helper used for closing file descriptors in a way that is safe even if the file is currently +/// held using `fdget`. +/// +/// Additional motivation can be found in commit 80cd795630d6 ("binder: fix use-after-free due to +/// ksys_close() during fdget()") and in the comments on `binder_do_fd_close`. +pub struct DeferredFdCloser { + inner: Box, +} + +/// SAFETY: This just holds an allocation with no real content, so there's no safety issue with +/// moving it across threads. +unsafe impl Send for DeferredFdCloser {} +unsafe impl Sync for DeferredFdCloser {} + +#[repr(C)] +struct DeferredFdCloserInner { + twork: mem::MaybeUninit, + file: *mut bindings::file, +} + +impl DeferredFdCloser { + /// Create a new [`DeferredFdCloser`]. + pub fn new() -> Result { + Ok(Self { + inner: Box::try_new(DeferredFdCloserInner { + twork: mem::MaybeUninit::uninit(), + file: core::ptr::null_mut(), + })?, + }) + } + + /// Schedule a task work that closes the file descriptor when this task returns to userspace. + pub fn close_fd(mut self, fd: u32) { + use bindings::task_work_notify_mode_TWA_RESUME as TWA_RESUME; + + let file = unsafe { bindings::close_fd_get_file(fd) }; + if file.is_null() { + // Nothing further to do. The allocation is freed by the destructor of `self.inner`. + return; + } + + self.inner.file = file; + + // SAFETY: Since `DeferredFdCloserInner` is `#[repr(C)]`, casting the pointers gives a + // pointer to the `twork` field. + let inner = Box::into_raw(self.inner) as *mut bindings::callback_head; + + // SAFETY: Getting a pointer to current is always safe. + let current = unsafe { bindings::get_current() }; + // SAFETY: The `file` pointer points at a valid file. + unsafe { bindings::get_file(file) }; + // SAFETY: Due to the above `get_file`, even if the current task holds an `fdget` to + // this file right now, the refcount will not drop to zero until after it is released + // with `fdput`. This is because when using `fdget`, you must always use `fdput` before + // returning to userspace, and our task work runs after any `fdget` users have returned + // to userspace. + // + // Note: fl_owner_t is currently a void pointer. + unsafe { bindings::filp_close(file, (*current).files as bindings::fl_owner_t) }; + // SAFETY: The `inner` pointer is compatible with the `do_close_fd` method. + unsafe { bindings::init_task_work(inner, Some(Self::do_close_fd)) }; + // SAFETY: The `inner` pointer points at a valid and fully initialized task work that is + // ready to be scheduled. + unsafe { bindings::task_work_add(current, inner, TWA_RESUME) }; + } + + // SAFETY: This function is an implementation detail of `close_fd`, so its safety comments + // should be read in extension of that method. + unsafe extern "C" fn do_close_fd(inner: *mut bindings::callback_head) { + // SAFETY: In `close_fd` we use this method together with a pointer that originates from a + // `Box`, and we have just been given ownership of that allocation. + let inner = unsafe { Box::from_raw(inner as *mut DeferredFdCloserInner) }; + // SAFETY: This drops a refcount we acquired in `close_fd`. Since this callback runs in a + // task work after we return to userspace, it is guaranteed that the current thread doesn't + // hold this file with `fdget`, as `fdget` must be released before returning to userspace. + unsafe { bindings::fput(inner.file) }; + // Free the allocation. + drop(inner); + } +} + /// Represents the `EBADF` error code. /// /// Used for methods that can only fail with `EBADF`. From patchwork Wed Nov 29 13:12:51 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13472847 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="yyMkw3fs" Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 91B5510E5 for ; Wed, 29 Nov 2023 05:12:56 -0800 (PST) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-5d1ed4b268dso23166897b3.0 for ; Wed, 29 Nov 2023 05:12:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701263576; x=1701868376; 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=2TJvklCbtLuSIRISVkztiSuoID32+GR+rmccCeVWR2o=; b=yyMkw3fs3nCpiUgL74Y5jRHnMmomVutRby/ghwGhq4Gkw58skLQ2rwODjfxuzvjtxh /2+HUkKnPrkRmID8QX1DwCt8V/cY9JbQ0Ph1RwgZovVr7lP6nI0POj1QE+I5jT/g1G1a NBHcsUxNDf4K3XTI2csPy9HAAM/AslnjR/oulmSMCgXSuGouEhH9kWrylmhTrAlZ6ruX EH9XVi7fXiJdWeAnipKR2gab60D+JdT94gMB8odYLEh+rRU/qhMR/g8r888tRgyzS7VS mDu46XfM4iE2cRImj/kH09pa5S3hBZU+fw0q+R9zqI3hPM9bXZdbH1UDJl7nIWOw1GDC tkCA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701263576; x=1701868376; 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=2TJvklCbtLuSIRISVkztiSuoID32+GR+rmccCeVWR2o=; b=isA4aOGPqpSm5rgOG5uc3j/hDJrsT+srTPm3O0zibX1szwPge3wikLXcn3Yb0Q9NYi BShT/if5U31hoR+FXqlRZi19zNbxwAmy3ElAjqEbkhvLXlJ+g0A+Xf/cudYhtcCjqERU EJr8caC06WQQSydkwEn9+lwtWIHi4TfLlBi5/ZGZadbXczaFSr3aIS9/dfRP/JMvz/M1 5l1gH4G/i9j0XK/leYXLA7Ft+BPIxOniazxj6TJNLXxPewzG1Bf5ACBpLsXpSIk1U7f5 7fPkor0JxqcmQPBQmYXJuwXIGrH9vZRrYCM+yXQS2b9OtgbZGqkS0OQdIqkuyABGrpyr lFyA== X-Gm-Message-State: AOJu0YywEDI/8BggljsYAEU2NhVKKNNdUvUZejJliFXWdK8EkjjeilM5 990zIuCIEGN+Jam0Jm0hJ41gZTZSPe3MKo8= X-Google-Smtp-Source: AGHT+IHUFKaHKKii09g7HUyZ+EjCiaihzyYW1lpvxVTE4olOwMBv+jZ8c8SCt3BB6Rr4low3G2iWwC8XUzaw9rs= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a05:690c:2e8a:b0:5ce:dff:f7a1 with SMTP id eu10-20020a05690c2e8a00b005ce0dfff7a1mr500330ywb.9.1701263575877; Wed, 29 Nov 2023 05:12:55 -0800 (PST) Date: Wed, 29 Nov 2023 13:12:51 +0000 In-Reply-To: <20231129-alice-file-v1-0-f81afe8c7261@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231129-alice-file-v1-0-f81afe8c7261@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=7830; i=aliceryhl@google.com; h=from:subject:message-id; bh=Gna2rPaAZxwA3tXNNtoQj3C864OEwyg6zxPLG5MwViQ=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlZzMyNVW1q4O9x1ivonWXMf7fKKfIjphoYoxIq mVR7gYG5H6JAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZWczMgAKCRAEWL7uWMY5 RsYmEACB9dMvCwDxJCJalR1hGrue8xlQCBRcKrXTmHgBJhJdUEPuY8WwazY0nvqF41fdD1T0Feh fBnee05Jy/wyLTSgeHvIKz4HU6SEaP4f2yOQQVd4twNWC+VWA9GIHMZ52S1MbruEK8Q7HZeuCdD hbJX1NSY0BVN9gudouy7gUMq6m3zTyaEaZRoB1/AbeQlfljl0rUy9kpTjIsXnrvuTw4qVdQBR2H ExFbiPpchpnXUe+fJrEDMvUnDtsGeStmEZ3EcxLJlzEMWNsqyanO0J1z0ZabT4QMlYl9w47mvzD IaOLMoJyN3PtTvaQKVmuWwO04xL7zmGXfqBbWAmOOWIuk5fu589MQ9qHjkTMB3ADt7U1BDICsgc rVNhfCSct2b7x8NAJ2WAJoqPmtDqBhYGcGCBduAVMmMwvRuP9Fx3ak9Grxjh1kpweqX8zD+qLEo fZK3w/eO4d3bWoNMfM320BDi3SFnrseBsjhDLaHjcyVSW8VQrzRUVFSYCvagYx81Xxl6vBHVRhW G842WeGHLaauNvonNZDijcHR9UoTiIKGSXcNCzyKo5Fmwv6onzWjh56nAb03cyW7zJv6X9F8l+Z dXrsL+OkE25KjYX/wY2Ff/v9EmLtEJ1uwrL5PbRh+zAguNPIAQtlyiV9DMqhPEWpXnYwXCALXpn 8YEVL4srVGhjErQ== X-Mailer: git-send-email 2.43.0.rc1.413.gea7ed67945-goog Message-ID: <20231129-alice-file-v1-7-f81afe8c7261@google.com> Subject: [PATCH 7/7] rust: file: add abstraction for `poll_table` From: Alice Ryhl To: Miguel Ojeda , 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 , Christian Brauner , Greg Kroah-Hartman , " =?utf-8?q?Arve_Hj?= =?utf-8?q?=C3=B8nnev=C3=A5g?= " , Todd Kjos , Martijn Coenen , Joel Fernandes , Carlos Llamas , Suren Baghdasaryan Cc: Alice Ryhl , Dan Williams , Kees Cook , Matthew Wilcox , Thomas Gleixner , Daniel Xu , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-fsdevel@vger.kernel.org The existing `CondVar` abstraction is a wrapper around `wait_list`, but it does not support all use-cases of the C `wait_list` 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_list` 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_list` is destroyed. Signed-off-by: Alice Ryhl --- 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_list`, 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 we could change it to store a boolean next to the `wait_list` to keep track of whether a `poll_table` has ever been registered. It is up to discussion whether this is desireable. It is not clear to me whether we can implement the above without storing an extra boolean. We could check whether the `wait_list` is empty, but it is not clear that this is sufficient. Perhaps someone knows the answer? If a `poll_table` has previously been registered with a `wait_list`, is it the case that we can kfree the `wait_list` after observing that the `wait_list` is empty without waiting for an rcu grace period? rust/bindings/bindings_helper.h | 2 + rust/bindings/lib.rs | 1 + rust/kernel/file.rs | 3 ++ rust/kernel/file/poll_table.rs | 97 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/sync/condvar.rs | 2 +- 5 files changed, 104 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index c8daee341df6..14f84aeef62d 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -25,3 +26,4 @@ const size_t BINDINGS_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO; +const __poll_t BINDINGS_POLLFREE = POLLFREE; diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs index 9bcbea04dac3..eeb291cc60db 100644 --- a/rust/bindings/lib.rs +++ b/rust/bindings/lib.rs @@ -51,3 +51,4 @@ mod bindings_helper { pub const GFP_KERNEL: gfp_t = BINDINGS_GFP_KERNEL; pub const __GFP_ZERO: gfp_t = BINDINGS___GFP_ZERO; +pub const POLLFREE: __poll_t = BINDINGS_POLLFREE; diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs index 578ee307093f..35576678c993 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/file.rs @@ -14,6 +14,9 @@ use alloc::boxed::Box; use core::{alloc::AllocError, marker::PhantomData, mem, ptr}; +mod poll_table; +pub use self::poll_table::{PollCondVar, PollTable}; + /// Flags associated with a [`File`]. pub mod flags { /// File is opened in append mode. diff --git a/rust/kernel/file/poll_table.rs b/rust/kernel/file/poll_table.rs new file mode 100644 index 000000000000..a26b64df0106 --- /dev/null +++ b/rust/kernel/file/poll_table.rs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Utilities for working with `struct poll_table`. + +use crate::{ + bindings, + file::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::file::PollCondVar::new($crate::optional_name!($($name)?), $crate::static_lock_class!()) + }; +} + +/// Wraps the kernel's `struct poll_table`. +#[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, and that it is only accessed via the returned reference. + 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. + 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 `self` and `file` are valid because they are references. + // + // Before the wait list is destroyed, the destructor of `PollCondVar` will clear + // everything in the wait list, so the wait list is not used after it is freed. + unsafe { qproc(file.0.get() as _, cv.wait_list.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. + #[allow(clippy::new_ret_no_self)] + 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`. + self.inner.notify(1, bindings::POLLHUP | bindings::POLLFREE); + // Wait for epoll items to be properly removed. + // + // SAFETY: Just an FFI call. + unsafe { bindings::synchronize_rcu() }; + } +} diff --git a/rust/kernel/sync/condvar.rs b/rust/kernel/sync/condvar.rs index b679b6f6dbeb..2d276a013ec8 100644 --- a/rust/kernel/sync/condvar.rs +++ b/rust/kernel/sync/condvar.rs @@ -143,7 +143,7 @@ pub fn wait_uninterruptible(&self, guard: &mut Guard<'_, } /// Calls the kernel function to notify the appropriate number of threads with the given flags. - fn notify(&self, count: i32, flags: u32) { + pub(crate) fn notify(&self, count: i32, flags: u32) { // SAFETY: `wait_list` points to valid memory. unsafe { bindings::__wake_up(