From patchwork Wed Dec 6 11:59:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13481707 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="AI+Adgcw" 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 58244181 for ; Wed, 6 Dec 2023 04:00:04 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5caf61210e3so95723117b3.0 for ; Wed, 06 Dec 2023 04:00:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701864003; x=1702468803; 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=fLPw/cBgJ0pNgJm8eE7aQ+KAHAiPGK+jutonPeJcGnA=; b=AI+AdgcwxGAT/n2PKzvPSetoX4mXzHwUQZH78WUiLgLh8vxyzFe7LKl+jh+9lsb0Re 63XcobEYgMvbzHeLBdVlj/XOeMIx2A+XhKgaaZaUflZ5JtnZfnMTObvFj9bdrSMB3xH1 f38IhHPpo+84/7zDxnZjyvsnaSYr2M9UAVDXeyBfqAoGUpJgMBa1Rbj6yHLn4nbk7asN VU99Q1+vATbY8/wR+jYfvUIirdV8zh0BAAd0XYP11iVi21O4gXN3VZQ00XeHCI6DbhcO ukv1lPVQtiJ4lXwlWV5Pql1E3KWfkjd69gk6uYOsBlNedkLTIRDJSlLx6YyEUOlH3G7P dM7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701864003; x=1702468803; 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=fLPw/cBgJ0pNgJm8eE7aQ+KAHAiPGK+jutonPeJcGnA=; b=oUwlEOVpwQhNa/Y9Z7hBA1ZpSGedRV20lp+jrrGGio3R1tdHvE588Gg0JFGwxcE2zi vOhVCekRFDgupiSR0VztPY91VUbKxKlpkIwqcZaA/TtZWeTVJLqW2sDa+pHrFdQqlKk4 Lez6fajazNMInq06PWFA+Ix5zHeySUBLdzv/YDz7UNh8c97oQKS1KscplLsd4SHjf3O8 e4TVEwpvK/RoQWGtpy42NrGvs14KxK/JXdPigK9BeAWRe9NztTODbdAb/9wGArh+gXqf bIhXVwQAN+K4jgUts8rQM/WMhfVHA3kSHL7FFWLNi++pe6Et1unj7nHaie9PT7zt76JW /gdg== X-Gm-Message-State: AOJu0YzxDx9+aI5Q+MqOxXENIxgfhz97OjkGtqWvYpSh+K1ojs7RRtln c7/KP97AO1VWAQ1b1xLJkYmuSLtALbRIUw4= X-Google-Smtp-Source: AGHT+IHnjTwn89g/3gZ0rUrhLKVW1ogZkP+ZdktJ5hw4l/C4A0NqN3tW5yq2T0PBUdfPKMUoAYRnTaxAEyj4kdU= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a81:b627:0:b0:5d4:1b2d:f43f with SMTP id u39-20020a81b627000000b005d41b2df43fmr8121ywh.6.1701864003433; Wed, 06 Dec 2023 04:00:03 -0800 (PST) Date: Wed, 06 Dec 2023 11:59:46 +0000 In-Reply-To: <20231206-alice-file-v2-0-af617c0d9d94@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231206-alice-file-v2-0-af617c0d9d94@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=11330; i=aliceryhl@google.com; h=from:subject:message-id; bh=fS+EISoJsOycGxjrMPeviEtEZy2GAzGu9mh6cNsegxY=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlcGI5tzCvzZUNwoppJ1OHi0LOvB+Mr/uD1R1Tp YwDNYjKrMqJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZXBiOQAKCRAEWL7uWMY5 Ri63D/9My5FdhgPod4Xr3p1UAu9q5kpqykQLHYRcH7ncR8PlKewdzPipKOCEfwI0oQGFEw5GRbL 11No1TgjpEkoMBzuX6PvprYnd+8zIguxfrEpQR3+3bOAPphdF8BC+sCUNCu4vQnB3HM1GQrw8EI M2pMpq1jdO7KluxGT62Xb/1bMl8Mn6lVi5KwlSgeTIW3MUcO197RU/FgfDK2nUGnp+je8KFP/YU itmXaTDNuK+QyyvLYtHu9QUBkRHjJ2Z+go8FbX5qKG55qs/uVJwCMcG1PFlP0XqTbLF1lv8jqW4 RYk3p1wj9bsQNhNdvst9nJhrMiMF6gldbHbCkqZVtxszBdhr0Lja/rbN9dx176Rtg1ROsywQurk 8eQrAaRnMGZt+KiAfEXOjikx9+n0zq5HT6UavhOKrRYPzSDYO9OeKiDYyTroEpG5UYAXRE5Pwn2 7wnXA/W86h4AunNf6fk0yYNkZV1T+ZKbUG/zfKzhbT8/S0spqV9w7ZfrslpumuTlHabbuJKy7WP GZdgbCa8UJ0Z26u6p4hA0ba81B4RJsdPdlTXQzjCX/DS7682+HA+GUwHyEXLPuwwch+0dfMdGeZ NrMBPKm9qDoPdWtRwqLQggxt7I522nz76jVRRnjyQxhs8PRsyaMp2tLNMr+UAjrVPagdJDLq2nW qPa2ycnWxhxhfpg== X-Mailer: b4 0.13-dev-26615 Message-ID: <20231206-alice-file-v2-1-af617c0d9d94@google.com> Subject: [PATCH v2 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: 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, Alice Ryhl 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 | 196 ++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 4 files changed, 206 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..29e1aacacd06 --- /dev/null +++ b/rust/kernel/file.rs @@ -0,0 +1,196 @@ +// 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: All methods defined on `File` that take `&self` are safe to call even if other threads +// are concurrently accessing the same `struct file`, because those methods either access immutable +// properties or have proper synchronization to ensure that such accesses are safe. +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 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: `fget` either returns null or a valid pointer to a file, and we checked for null + // above. + // + // 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 { + // 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 safety requirements guarantee that the refcount does not hit zero during + // 'a. + unsafe { &*ptr.cast() } + } + + /// Returns a raw pointer to the inner C struct. + #[inline] + pub fn as_ptr(&self) -> *mut bindings::file { + self.0.get() + } + + /// 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.as_ptr()).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.as_ptr()) }; + } + + 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`. +#[derive(Copy, Clone, Eq, PartialEq)] +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 Dec 6 11:59:47 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13481708 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="hS+fTvAJ" 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 B5C511BD for ; Wed, 6 Dec 2023 04:00:08 -0800 (PST) Received: by mail-lf1-x14a.google.com with SMTP id 2adb3069b0e04-50bfd790d42so2453304e87.3 for ; Wed, 06 Dec 2023 04:00:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701864007; x=1702468807; 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=Q4h5TtcDHBkvr+2eo+ds5tqRTeDmTOKySCbN+YJdlDo=; b=hS+fTvAJRnWwcfurJLNGFWS3IHxZsryQ6Rn8J4ZuDm1MWhcHDrB0v1pt5A0oQ6Wpyc BQ6Ij/t6nFxu0p8cgH7SSTE/Cv1wisxZ4bebeQroSJYUTBRYsZTC1WJKq7m5zcg4cFxA EOvuY/81Bs32oUxT0NcSsANhjvBwpqi1wvOgpfpS6clbTrAx726DbkRSraGFWxcFaGF9 9GYlZ2DWjpuEY9DZfG2onAEiqLvnMZm9GKrxItAHq6vM51fuubC08Wwtq+fRfnLUXf9O kCFJw/op40SfNHqmNotit4q5Kc+ET10zTpDr8kYGL3Bg0qpvZsGYd4UAZ7Jg8Jr9+KXY 0upA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701864007; x=1702468807; 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=Q4h5TtcDHBkvr+2eo+ds5tqRTeDmTOKySCbN+YJdlDo=; b=Qu9HIqVYDKL9mzWgGSC9sJZe0G2RJfnG6tnzQUYSF9KC45IAq3xQ8E/ZLw0RuG2QR7 F3exj7dpAwuL2VNSG9VrTEHbdWaw0ZzsTeaSHT9CCJY6ZAhHJicy/VsoFonrVI9x2Q3B fBzfTHvCgxDzY0De3Z+mjYjtqcFVzCrR8Q67DMYCyrn2redJfUYBHEm7dbDWWBOMyKk8 BOYXTtB4isoQjA7G9UgMjXcANo8rsf8oepGrpbv19H6iDOAmhln0CiWjkPtZKxhMxdZb 3zO3/ddQ+MOtl7UWgzomaVoLRo/DJXfDCETJUN5P41XWlB3HalO8VnXQ20qGeJqIbGBY wVrw== X-Gm-Message-State: AOJu0Yy2pG1oAPun6ENlVKEBGA4FXlnzp6speEpxdPPl3L5HFjm4TQq0 uPVxKpQaSD31JP1ULPoQpyT9iI0v6B9MJ3A= X-Google-Smtp-Source: AGHT+IFUlZXxN3xc9glrJ6y1laVME7KrGF3BWN6lxwqRCFh67SrX2kH7P2bsaNKZLkZviIk70A8VgUdmvGVDgr8= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a05:6512:2810:b0:50b:fa87:ec7e with SMTP id cf16-20020a056512281000b0050bfa87ec7emr8592lfb.2.1701864006500; Wed, 06 Dec 2023 04:00:06 -0800 (PST) Date: Wed, 06 Dec 2023 11:59:47 +0000 In-Reply-To: <20231206-alice-file-v2-0-af617c0d9d94@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231206-alice-file-v2-0-af617c0d9d94@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=6664; i=aliceryhl@google.com; h=from:subject:message-id; bh=Vm1W1WVMEXarXNOCcD4vjJx3cnrZOnPrxh0AiYWusOY=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlcGI5vSsFMnG38B3rWjePZ20b8+wpfFKfcctgA AqrR4pi9vaJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZXBiOQAKCRAEWL7uWMY5 RhjUD/9DPZhmptqt8Yroautfb0PINF75GwF4YPTEeGm0XoT5DCH1WcBy03aPyiSnRkYxIr7fYai GLxLHjowK7BpyV+WajJyrBODTVO68sorORLofCHjoVHJEEDc/iczDs1NEunjShzU8dw19Kw2MG2 G77RmtsMjIbTJW9xhEqyHkRCJQ50qeaqOcao+NMpWFmiN4MIeW4i1TzQjkj8v/RW4FrxSHybv6g 4qOckgZNBsTKix4WqoxtuImFWZkBflfoC+54yPEsgpZUU6cyNP34Ur3LJA04s4wygkvyB4LZ2bI 8zaU1SO9ByC8rYGIjbi5uZVjoZ0Gm9GMHCxeJEZgR45bN8tXut1WnpiQ0WhSzmG86HPmr/ZqqAv SiBt85Qfmhu5+f5vRry13WuhAmThCT4RZ0R7lvjocrhJAlgM5jY/hSfL/aqiyAmtWFJeelNbd8K /SXoyZ9WNFsalnzfYMYmlKrN+7ZE8Pb6J/cxcHYBClVUeIEmoGLdSZLr8FwJZZJqc8EihN4tTeH 1E0n9BRU6OgkI4i19R/Mq2P3n1tyDExnRVgWwiE71sidFDqWWNLzeEz7lHb4GjJWzqsrQoD2xHY +PIerDizftBdGkiP+KWRYeSvAxUO91VfeQyOIuv+grXGLBsFUwc06+0D/9FMn+llXafWX/SYbOR s3mvrbcakKZ+eyw== X-Mailer: b4 0.13-dev-26615 Message-ID: <20231206-alice-file-v2-2-af617c0d9d94@google.com> Subject: [PATCH v2 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: 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, Alice Ryhl 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 29e1aacacd06..a88140794a8d 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}, }; @@ -151,6 +152,21 @@ pub fn as_ptr(&self) -> *mut bindings::file { self.0.get() } + /// Returns the credentials of the task that originally opened the file. + pub fn cred(&self) -> &Credential { + // SAFETY: Since the caller holds a reference to the file, it is guaranteed that its + // refcount does not hit zero during this function call. + // + // It's okay to read the `f_cred` field without synchronization as `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 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 Dec 6 11:59:48 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13481709 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="MC5uVyIO" 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 612A1D40 for ; Wed, 6 Dec 2023 04:00:11 -0800 (PST) Received: by mail-lj1-x249.google.com with SMTP id 38308e7fff4ca-2c9ef4b6ce4so38406761fa.1 for ; Wed, 06 Dec 2023 04:00:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701864009; x=1702468809; 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=5tmt2N0VwVmdbKWnzafXFaHLq0Qhxrru0xsHWYV+xBQ=; b=MC5uVyIOsMTon7BV7fc3jPhv1C3u2n7EeXmEByS6idGaBlJRRfk41qZQqUeDos6Es2 jWWjJ9AwNYekJrBUniPm9MdiyfE7SIMzSDW2ZpYczyV3t9yXj198wdyAid75yf1HPZab lh/FMQ4gB5UcgeyTcmEAcFo0YqYYRov8W1yENr7ofWRLFIUEk0IcqFuEMxQaKkRmi92n yOed4qz4G/eAHrjtoUjFXQAoVzru2XopYwNjqUHjcodzCehFbPvfXHOtu1Hj+XoAHu1a +Cza7F2atj7oZeG75s3QrXkhGaWIsM7VD98SD0d4T1RqG8Un70PoznLTMmHHFLJzT06p NCLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701864009; x=1702468809; 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=5tmt2N0VwVmdbKWnzafXFaHLq0Qhxrru0xsHWYV+xBQ=; b=Yh9QIBsISXfUkXNSRs80wY9GVFxhAoHNgz/HUfPIizdGTf+YX6krC5C9DSnCoEA/S1 5p+I0RNqQ0P0qmy2/EB99XxTBjrcrEz/kiyF6l69Yl6D7H+ADNB5qWxcILPOg5wm58Xm DbmFMbxJd/UvVcfMCgL4aktas0xeZVP7aNNIJ7edvE+j29sl8l1wEd9+v/ZJ7QY42b1R 5y/eoReN5ruSeEiVa+FWm7wvcW4aaALcWhWfcMLXI8XqTgHRCRI2tMIflaujoPm0nXrF Lbw81TotejceVR+dLi0xO+TRq/x3ZoDgWfL2wPpSaVsOrUzOhylQaom55WU/6qmO1+h3 9RjA== X-Gm-Message-State: AOJu0YzsDqlWIrFCGyJRruIPP3PiO7W3M3+SjEcYxPmXz1s9HRp7W4/F c/b7pHwHEL8g6SeOQvxo/sTZqWpFRhYRNio= X-Google-Smtp-Source: AGHT+IF1Lt3w7IGohb8BJXAbz6Df6e6l5YguB/57GMGafOpE3DiFazVzW06PvsO5/HhTDTey8czloJ5EVATtO7U= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a05:651c:198f:b0:2c9:c560:b33d with SMTP id bx15-20020a05651c198f00b002c9c560b33dmr12324ljb.0.1701864009526; Wed, 06 Dec 2023 04:00:09 -0800 (PST) Date: Wed, 06 Dec 2023 11:59:48 +0000 In-Reply-To: <20231206-alice-file-v2-0-af617c0d9d94@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231206-alice-file-v2-0-af617c0d9d94@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=6345; i=aliceryhl@google.com; h=from:subject:message-id; bh=i9YFQd6/Yd51tifYQVq47mzEm0F+9nhoDryKROmvL1c=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlcGI6vioB5c9v/JO7VFZ39pDeZbR+KMKypQ7I1 emAZrbqhQiJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZXBiOgAKCRAEWL7uWMY5 RgggEACMdH82FbZQh1iUxaANeGdNilF9X4MKsgLxfGa7spb/o/Px65ohICqwdeemQj61Mo2sVs2 IX95wn/ys9kkcUShXWSoyjjlbi0WGtW2jbQ1AxC8xbV5ciwLzwUhDhN0+ORORCCBxGZBIIbK8zQ kFUt6lWfrAaj46D/xRqQruRfuTp/wowe6cBgAvYhu0xG3bm8EvzIo1BV4M2ooF27LZbzsMzaCFr 47z1VwvZerHWae1ggPe9Wdh7GBo8WZvlR6TeMOaptT0AorZVv5x7/j4S2DSSlbW03jhkinNb23G Z6kiwmw76NUxp7kRPivvY03Du1C8G/9xRLWP8K+wW0JfQAzFNrkgQ6Tr0QO7sm6ee9Fa12uk5RH uFj6KxdpsqUy4Ar3Kt/G9zs6TcOFQcTgHIZ5Ctoz15jK/CiaS5Syj6CLu5o8kQqQIfKUkEPO1if 6E5Qixzjoef86aeXPLhkY3OpLq+HfMByIw9BaV8BiG7atl9nDcwxMNOw2tQoA0DK1WGGBjDiOAL ksO5iw3YvQJo8I315E/tQNivQERvKozjnm/9gD2bPZrivSqhNetO0sn6+/nUf6COGBfK8w56mT0 ugLregUoJn3eTjhiwi3cDK0VcRwIWqboCSeCeiVEq1OcbdfWDUpezSU9zVHwjDl5CUSj/0GZl5q mVgi+X+HPz+9vrg== X-Mailer: b4 0.13-dev-26615 Message-ID: <20231206-alice-file-v2-3-af617c0d9d94@google.com> Subject: [PATCH v2 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: 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, Alice Ryhl 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 | 79 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+) 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..6545bfa2fd72 --- /dev/null +++ b/rust/kernel/security.rs @@ -0,0 +1,79 @@ +// 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. +/// +/// # 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. + unsafe { + to_result(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() { + // We can't pass a null pointer to `slice::from_raw_parts` even if the length is zero. + debug_assert_eq!(self.seclen, 0); + 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: 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 Wed Dec 6 11:59:49 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13481710 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="ZwVFUyXa" 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 41BAA1BF for ; Wed, 6 Dec 2023 04:00:14 -0800 (PST) Received: by mail-lj1-x249.google.com with SMTP id 38308e7fff4ca-2ca0fbea7efso20092631fa.2 for ; Wed, 06 Dec 2023 04:00:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701864012; x=1702468812; 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=HX24dnaE+B8dQD9+a06Kja65k0fpP3j7QxTbOLGSMKA=; b=ZwVFUyXaHyrTd7KDatNq1jU8PrRUP1K+jmKb/35ykTvXSDll5fYj3ec/6n89kN4pZA GmCrdWokguzi68RHWOss5O40OIjEbUB3/bahur9YclJMQK5eALxtTNU1UvwmG34rM76a EO8OqqBSQoa3oxFwQeTFvsHfI5tLOavWstbADBaAxbXkTA30Tu6/ZgQXlQK7gwdhploP 3+au7KiqsGeAy7ichAUezunAWn0tewZgHjAYivcNq9lQDEerruXXgOjnH1otceShq7BS DATU8R9aCId+CJzZkjgAk25h841VwH0Cy6JmLkRtoVeD/0m08tbVUY1K5xIwIt1VHyYD mL2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701864012; x=1702468812; 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=HX24dnaE+B8dQD9+a06Kja65k0fpP3j7QxTbOLGSMKA=; b=CChi2Q8G7Esm/7MkC6yjXBmsMMjqWUsV3Gx0280lgGT75/SQ2Kg6e5fkQYjjZDw8hC SpcADZLpkqFs0X+4+QPIWjeWaCCHUSQDNSAYFp9fAaF8HuR+SsMEmhIfKrSY9c+wFwM8 DSv/LarFdVyp07jAnenKDy84eAD6ZLCsLXKQzpwKImIOdvsC894YT46NeXNEOulp+gZK qxbOQgEH0cmqxl7OeDyU+d4qCqc6nadWB18qyjAlfnuLhC2jZnkOgh1zIRY0VkcG1Xm7 1pPXkdF5nxLtt7Ic+diIZwwwlCS4gSttIaqJ2yO+nh9GkxUTz+iMZFMkTnnKD5FzZqru PAgA== X-Gm-Message-State: AOJu0YwuedCCHXbXk4mnHnCDJHNO3EVM6TV8RutZgPIqvnkvFAb6byDG 2CmkNtfWLAAXnpmNU1ThySOke8I4OhBEWcw= X-Google-Smtp-Source: AGHT+IGG4rfer9+wb6zLaC0ZQniLETPL4UJyJZrGKw6weysjcfPAL174MeYMqyYdp5VvSFl66sr88hufBfW61i4= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a05:651c:505:b0:2ca:1c35:b1ff with SMTP id o5-20020a05651c050500b002ca1c35b1ffmr9827ljp.5.1701864012431; Wed, 06 Dec 2023 04:00:12 -0800 (PST) Date: Wed, 06 Dec 2023 11:59:49 +0000 In-Reply-To: <20231206-alice-file-v2-0-af617c0d9d94@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231206-alice-file-v2-0-af617c0d9d94@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=5468; i=aliceryhl@google.com; h=from:subject:message-id; bh=LT6/jKb5BgD0V7zv4tSIyAeTufgj7OW+bdLBEekMnp4=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlcGI7V8wurNQW4hYt9klw3U62E/cywt/0hlVdD 6nZ+2BMuJ+JAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZXBiOwAKCRAEWL7uWMY5 RhD1EACRGMMcqA4bUhsKfa9wFlf609ZQqnQQSJRSW8UOl43LGBLRRCL8vRet9dopXE8coV3A43k 9xPvGwlpCwcx5qhpqcNYFfxgLVwC0cZU0F4GlNQAI3D2B+7lwY5gy98+u7EW579s1hT0k5QTtol AqDj3UTgC662YcZ83oGzowWKvyjD+ED/NaAbYM84wFq69be2RMQzSzsF7tVO43FrBObluw8c50Q vT9PurHi9w9wkaCK2vaNMtDNJn9ESgIqHp1ix/0Ov6kh6cYNoqSAqbX3IfkOmgsWbpk99s4EySY nOokKzDDI3VMgQJJ5cFCcs56i1TxH4fGIFAH1WiWXV0YAoRgzX3XJIy7yW0lzio1Uboj3MuEzK/ kjJ8Ng9/YIk9jyNlLpfmwKcgfw6Degxrf5yUXhK648+2T+Ykpo7XPNZ4u/xp/TWPoojZuqMmkbF 8yl+hC2IyTltKFWXzGfrvPA6UffcOttzlh7gqqEhApR1cts5yrM5/zpfe++RMpepig4Td4CXpqz m03C+jgn47zZbgf/1nJf1gERuLlvVtADzJ8h/J4lxHwfGPeeYFsDFElYK2wHsOJjh1Nn0e6/y/j OW8JPxYsk03Vnw/QFYZl+7TVaEhRdwTW3noYpf/xBKovm1LH1sXNVEhRWTmKTOkWw+5YsSiw//T b2tP+Dcxiqhjlow== X-Mailer: b4 0.13-dev-26615 Message-ID: <20231206-alice-file-v2-4-af617c0d9d94@google.com> Subject: [PATCH v2 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: 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, Alice Ryhl 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 | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++-- rust/kernel/types.rs | 10 ++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs index a88140794a8d..2d036d4636a0 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/file.rs @@ -9,9 +9,9 @@ bindings, cred::Credential, error::{code::*, Error, Result}, - types::{ARef, AlwaysRefCounted, Opaque}, + types::{ARef, AlwaysRefCounted, NotThreadSafe, Opaque}, }; -use core::ptr; +use core::{marker::PhantomData, ptr}; /// Flags associated with a [`File`]. pub mod flags { @@ -193,6 +193,70 @@ 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. + /// + /// 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: 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 fd_install(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`. diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index fdb778e65d79..a4584d6b26c0 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -387,3 +387,13 @@ pub enum Either { /// Constructs an instance of [`Either`] containing a value of type `R`. Right(R), } + +/// 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 when a type stores task-local information for example file descriptors. +pub type NotThreadSafe = PhantomData<*mut ()>; From patchwork Wed Dec 6 11:59:50 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13481711 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="No3XEkct" 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 0C585D47 for ; Wed, 6 Dec 2023 04:00:17 -0800 (PST) Received: by mail-lf1-x14a.google.com with SMTP id 2adb3069b0e04-50bf87fcb29so3018122e87.1 for ; Wed, 06 Dec 2023 04:00:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701864015; x=1702468815; 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=ex3XFDrwLoR/nkrJxHEDseFTPk4W5eJ7YuU+mGVpLsc=; b=No3XEkcta1a6FXzlMWKhKCqGE75LE+SEfRNsTmERIWjCzRa7NuaHpzxJrdR7vbZjtf GqyMHe4CmEsqr4z38KG1OMOBqBlneNmRNACrobnjHXxJWm/2+SE67uVDCQrSdbSV5oqp +ekkN9btKDV6bax6xMnx0a/Er+9Wfsk+a1U7h5Nje7wumV35TTaRs54FL51N76dGsapz 2Cq/+COiZHGKyNN940AHyvwqQLI33YrqOu5T0Qe16h5NR9DtfnND3gC6e6INkCf/0vQp VK4LIV9vYGl9rS5/SwVo/25dobAq2fry7F4AwBCyGZ0IkJfsNTBWNDhniVRhFLRUOc+o BHHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701864015; x=1702468815; 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=ex3XFDrwLoR/nkrJxHEDseFTPk4W5eJ7YuU+mGVpLsc=; b=kRDEKNl6kzGsoN8GUIvoR9EdPm30J4BdknmRbg1EaKDr4G1Wr96xWvrWPVojqdPan0 UWtVbIeT1l+81FoI6QWZD/27dYIDRjOV3j6ErLIgza42gfWrjj6Pv/5fQXXm8AmC5Bms MHORzamOL59nVifWLw1S58zJ5RLKKx5ou2q6EiZ+kiiyDELW0EnZVHLgWb/FqOPXewPE dG1tAAnHZM49xHfXc6F9Eja7Goi1byc2h9oXKKX/fl9sYFf0zl4HbUUjgUVc8P97dc6a 7C5YZj78TfGYYAjTeLfjIDkQhHjvaBqwih3IsG6ceWu5NBUJhmJrGat1jgtczcr5oiTG ADNw== X-Gm-Message-State: AOJu0Yw4QJYHo1IkLihFlZNZ/Ptg3jrHJV5EgSd2f0EbduCPhWF//5au P51BwYZicCF/HJoQALC5NttpVnm85YE9fOs= X-Google-Smtp-Source: AGHT+IFV2wNOA6+gAb0Zk05c7RUksRnkiW/1TaPoCAbGzoANtA5hmVSDV1Q71yj0+RCjgkjL6+0iRrX3nAhgc10= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:ac2:519c:0:b0:50c:60:eedf with SMTP id u28-20020ac2519c000000b0050c0060eedfmr8111lfi.9.1701864015140; Wed, 06 Dec 2023 04:00:15 -0800 (PST) Date: Wed, 06 Dec 2023 11:59:50 +0000 In-Reply-To: <20231206-alice-file-v2-0-af617c0d9d94@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231206-alice-file-v2-0-af617c0d9d94@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=nSD1RHFiSgmdte3sXm2CuEd4KKb3h+aVVxb/9ER4D6k=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlcGI7qpjyrfDIi3Px1DbLbn4cXTm32iM/TT6P/ Aa9cVXYk7WJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZXBiOwAKCRAEWL7uWMY5 Rt4KD/9zI0mvZ5UC1bcQXnp4cBkKv2N4XTt6BO7BMq1naJpgTEJaDGfTzxV2Uq5619DVuFsz4Sr HrLo0Z0UYqnhHmh82PAB5Mc1RnUbdqddzLYfUqyCsMDBqvmYpsXbAVzgdF9aciAp+yKxPTBKYs7 M635Y+sq6M/6b9v4n1mDfJfqBmGj4ER9HNRZiBohtTTEzQz+JdN/Di9ZfJ4qZOo89TCHtIBIj61 ntGx31Lp1q+HoiWDkBBFPmtAhlmOm2A66Yoag51GLHyZuDqMMaem6cbo87Yzi+GIOFD0UbeW+Ka pGcPxh/4oDKqET+P0BfLbPLPQ+39M5uVpvaYFJSbEJzgnCyf4OlXlVzeH2vJhepZWZr1XHeeL4b c3saxXMSctJbBcwc6VMW7Hyzb3ysaVkJJeKw06UPfOtbmgDzYbBTHJHkizhEAR0GjaKDhMhqpT3 aJlQmEvcxFMgkFzkb1J9YFRD+rrbg7v0cysTGlWfXLUFAL6W99Sj28iIgGWrLkq/fcrOcRI24Oj u/9cbTDF1NGsMxCHCTYys/pWCmUq6VBgXXCq3bR0CKhnwZsHqgKE9Qgp3Dt6xlg2RPfrr2gpdPD JpktRE/UEIRg5n/cozS8rnvaiCtyl00XSSDcaDx8jX9peeQZ31LKgLNQifkTGvhNoEEwngfmVFo 4YilCXYXvA4/Mag== X-Mailer: b4 0.13-dev-26615 Message-ID: <20231206-alice-file-v2-5-af617c0d9d94@google.com> Subject: [PATCH v2 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: 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, Alice Ryhl 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 | 68 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 116 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..7a3a07660af7 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,32 @@ 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: Calling `task_active_pid_ns` with the current task is always safe. + let namespace = unsafe { bindings::task_active_pid_ns(bindings::get_current()) }; + // SAFETY: We know that `self.0.get()` is valid by the type invariant. + unsafe { 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 +178,41 @@ pub fn wake_up(&self) { } } +impl Kuid { + /// Get the current euid. + 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. + 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 userspace UID. + /// + /// 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 Dec 6 11:59: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: 13481712 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="O1AvjKra" 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 10825D4F for ; Wed, 6 Dec 2023 04:00:19 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5d67fc68a82so75733597b3.2 for ; Wed, 06 Dec 2023 04:00:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701864018; x=1702468818; 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=Y2Vb6XnFBNT/vwoBLNsBzMzVsVm0lPXO15u2hoL76Ng=; b=O1AvjKrazk6JmWuKnwFvjvFOrcx4IlRwMZm6SezcMbuA0ogM/A7jdUTYliqEMvzx3c mb6KP+p5xZztquD9a9YwpgjseL9pHuwnJcBSZKiSg5mFcFUEmraaqwOEtKXUmmc/ALN2 iNwROSxxKlkxEHG9BiDxsTEexNdO39d+CGsT39MECcofQBH76fSyFKRgRrpJmoUE1xu8 VgThICM1i8aEC2ezDe+4NpAcmWELSUDeZTUIAt4Kbh2JITKKbRZjaISx4T2ylmNnXS50 AfnvYpSmLts6/xTBukJZ5PEEJNddOxC+xahwj5a23aoSjqaBm/OiKOJAiBvoiKCfozIi 0/ZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701864018; x=1702468818; 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=Y2Vb6XnFBNT/vwoBLNsBzMzVsVm0lPXO15u2hoL76Ng=; b=XfYmdpjAWmp3D7GwYgoQRHPrsiM0H0paBl10v702LP5/urUz+ewbyCLS/1pmOQGNvQ y5Wrigv56hAkLqsIShPqiCm0+vb7xG1W+gyCV3cYTLneXHQumkO8AASOaDnY5LLM+q7E Hpox80UHiUJpQ79Vedfw52f2CSRLdVAG6AcVurz1avSss/MMP4nTGe3eHFJLWT3sAzTj ToB8KqCgZ/heIkeXzNGNN4QSy+d6fCo3UzIQcufQr4ifYT+F8FJ+q8eiNoYE4dIEK4Xo 3dgj2q6x348PGdgkwh+hCrkEnjInxsoL2JzMJLFTeMMtZzyCvhJK6OE0eeh0AyHT7xdG q39g== X-Gm-Message-State: AOJu0Yz9SgELZ7GTSblGMMAHoNBksqBhkSkhqdqinnhOTddTExmIcfaW Rpikl0Kppvmw3oA3qdsDsoKjOGLOQQtx0og= X-Google-Smtp-Source: AGHT+IH1KoLuU0UnF3Vl2p4fqkjvUSHg4h8gImLmOYiYyXeo5fjtozQfAlbJM9wNb/t/px3KgxT+M8tHN+mI1gw= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a05:690c:3244:b0:5ca:5fcd:7063 with SMTP id fg4-20020a05690c324400b005ca5fcd7063mr8743ywb.3.1701864018242; Wed, 06 Dec 2023 04:00:18 -0800 (PST) Date: Wed, 06 Dec 2023 11:59:51 +0000 In-Reply-To: <20231206-alice-file-v2-0-af617c0d9d94@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231206-alice-file-v2-0-af617c0d9d94@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=11026; i=aliceryhl@google.com; h=from:subject:message-id; bh=F2Jb32LY5rLgggxlU69aJ4MLraHZcw8F9PxXVr+ujgk=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlcGI8kapIc9cnaSVwJuC2VIEOUtI/5p082wwVl FSNa5NwlgiJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZXBiPAAKCRAEWL7uWMY5 Rq5yEACet4Qi4t6lEVcVeB70AKFLpl2XJ0Yd8QYFqbNl9L3OtcXJO8i/Be5iXFWITgjxC+jxYBz IllRj8e4crAvleRpseTOi190hejnKvXyxfoiCNlSdbk3DeKvzCT16vCWb3i7F2tgEV5NeROJ/vh f6K1T+F+h6CP8d0+D7HOnF9zFfD5eHhiv0yE+0z9mitmQagZrwMaHQFCwrDwrl9KTqtxb+I5Xnd Ko/2V+HUbZP1asNsWP3rpbXEDaAUHUCrOZUw1K/yklSKNwveUVd5Phe4HSgl0uoc7Z9BVoDjzR5 fcPOHQ3krlznkVkXMli8adCS/TbRPvN8SwxpciChIsFWAbM2/UWMq7V0ochGDJ2GNu6env+HsVM avyWbMq4nxx8H7g4HSb+zIVDw/ohZozC/xJ+hnbJFNsP0yl53B4tv3jrPV0t7mA3nKqcna1ZaLr UZqBZGOH1rIoq0Ga3c5vXgHBHVHLCvg5uqcZJnM0YL8Wz4EwRNtV5ylFMqnlKLqcC5cD8AHisdI m7ZR2f6cjRxPVDK4FKcfarqnBBXv/wbMmQOVlv4KEmQbXT/4md6xOldpFcHMCYuxCCKCdZkUFwj VazJw8Wj7C5NNx2Jbk/NdE9E3NwaUVYjz2O8ozCY51cPEdT+Ezz5gh9kW057qkvZrqT83RLqHhs /n0JIs/3McjwR4Q== X-Mailer: b4 0.13-dev-26615 Message-ID: <20231206-alice-file-v2-6-af617c0d9d94@google.com> Subject: [PATCH v2 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: 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, Alice Ryhl 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 | 157 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 166 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 2d036d4636a0..eba96af4bdf7 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/file.rs @@ -11,7 +11,8 @@ error::{code::*, Error, Result}, types::{ARef, AlwaysRefCounted, NotThreadSafe, 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 { @@ -257,6 +258,160 @@ 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. + /// + /// Fails if this is called from a context where we cannot run work when returning to + /// userspace. (E.g., from a kthread.) + pub fn close_fd(self, fd: u32) -> Result<(), DeferredFdCloseError> { + use bindings::task_work_notify_mode_TWA_RESUME as TWA_RESUME; + + // In this method, we schedule the task work before closing the file. This is because + // scheduling a task work is fallible, and we need to know whether it will fail before we + // attempt to close the file. + + // SAFETY: Getting a pointer to current is always safe. + let current = unsafe { bindings::get_current() }; + + // SAFETY: Accessing the `flags` field of `current` is always safe. + let is_kthread = (unsafe { (*current).flags } & bindings::PF_KTHREAD) != 0; + if is_kthread { + return Err(DeferredFdCloseError::TaskWorkUnavailable); + } + + // This disables the destructor of the box, so the allocation is not cleaned up + // automatically below. + let inner = Box::into_raw(self.inner); + + // The `callback_head` field is first in the struct, so this cast correctly gives us a + // pointer to the field. + let callback_head = inner.cast::(); + // SAFETY: This pointer offset operation does not go out-of-bounds. + let file_field = unsafe { core::ptr::addr_of_mut!((*inner).file) }; + + // SAFETY: The `callback_head` pointer is compatible with the `do_close_fd` method. + unsafe { bindings::init_task_work(callback_head, Some(Self::do_close_fd)) }; + // SAFETY: The `callback_head` pointer points at a valid and fully initialized task work + // that is ready to be scheduled. + // + // If the task work gets scheduled as-is, then it will be a no-op. However, we will update + // the file pointer below to tell it which file to fput. + let res = unsafe { bindings::task_work_add(current, callback_head, TWA_RESUME) }; + + if res != 0 { + // SAFETY: Scheduling the task work failed, so we still have ownership of the box, so + // we may destroy it. + unsafe { drop(Box::from_raw(inner)) }; + + return Err(DeferredFdCloseError::TaskWorkUnavailable); + } + + // SAFETY: Just an FFI call. This is safe no matter what `fd` is. + let file = unsafe { bindings::close_fd_get_file(fd) }; + if file.is_null() { + // We don't clean up the task work since that might be expensive if the task work queue + // is long. Just let it execute and let it clean up for itself. + return Err(DeferredFdCloseError::BadFd); + } + + // 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) }; + + // We update the file pointer that the task work is supposed to fput. + // + // SAFETY: Task works are executed on the current thread once we return to userspace, so + // this write is guaranteed to happen before `do_close_fd` is called, which means that a + // race is not possible here. + // + // It's okay to pass this pointer to the task work, since we just acquired a refcount with + // the previous call to `get_file`. Furthermore, the refcount will not drop to zero during + // an `fdget` call, since we defer the `fput` until after returning to userspace. + unsafe { *file_field = file }; + + Ok(()) + } + + // 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) }; + if !inner.file.is_null() { + // 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 a failure to close an fd in a deferred manner. +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum DeferredFdCloseError { + /// Closing the fd failed because we were unable to schedule a task work. + TaskWorkUnavailable, + /// Closing the fd failed because the fd does not exist. + BadFd, +} + +impl From for Error { + fn from(err: DeferredFdCloseError) -> Error { + match err { + DeferredFdCloseError::TaskWorkUnavailable => ESRCH, + DeferredFdCloseError::BadFd => EBADF, + } + } +} + +impl core::fmt::Debug for DeferredFdCloseError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + Error::from(*self).fmt(f) + } +} + /// Represents the `EBADF` error code. /// /// Used for methods that can only fail with `EBADF`. From patchwork Wed Dec 6 11:59:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13481713 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="wDMV9Mes" 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 4CE62181 for ; Wed, 6 Dec 2023 04:00:22 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5d064f9e2a1so95645717b3.1 for ; Wed, 06 Dec 2023 04:00:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701864021; x=1702468821; 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=kbE17RqH74sTr8G98jh3hq0E2+R+y1BNKFvou61O0+I=; b=wDMV9MeshFO6aZZl07J9DO/uXq2MCY576huZTrqxAhmU/tXsisul+3HkhGQYdxJ62y 4fWQLYw26tux+uVog7c/RkcygPjdiIhXhRSBxEVPDjLzPdBpmjqGVAHKyvgxf8huCJ9b muSq8DjZ9CzTMy3e50sH7H4oew0d5DONSpE/G3iCn+O10DI91hAQ0SnsfOLFPWnNEMk/ ic1MmWjX8ftYQZhZpUA0Z+OqkBO0rftHdaM4TOssNOg01vkYgYpDE7OAzeI1sOyb99/t JZ0lgeMAksHkzYijPvOyV/2uWaDyRbMZiQJ4ey9Nmcxyrf//rJWNEyhDupezS1Mkwcg0 O3Wg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701864021; x=1702468821; 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=kbE17RqH74sTr8G98jh3hq0E2+R+y1BNKFvou61O0+I=; b=vcY4XXsR0fQHFEAJ3HESghwujWwo3HBuOuOLG7kMOdsh3Dcc4l1ZxepR2sshvhKYol t+lFkRvrYSd4SmElfE6uclihdt45BehXBslxcsXM5jr06Yp4P06TRYo7mVRotsBJF27+ Rs7AQkCrUnagCeaKQ1edjqFrVNs0xXjm2Fd3aRJx7rhaYLypW4AabBux0QdNdpiedICX CX+XIkHWdOPIFdjmZ3t6qq2Sgpde3LSFz+o0tfr9lgUhESRBMexM/qHNPTtg1T9oRVU7 xzOGrOMM92GaiadaImZK3Ep63Rm8XlkcWM/rwDHdyD+Th3Cj7LoaXjUh3bVb2Mm0GdhB IYzQ== X-Gm-Message-State: AOJu0YxyH/58TFZ1vQrka6TA853eYEqYql5MQmg1nHOEOkx90R4qJ+UG DWnpzCOTkW14Xxl7zqLJ9J6l5asy3Q9E3Yk= X-Google-Smtp-Source: AGHT+IFZCvODtOTMGqKTf2LP1GyZaL8gCaAllaeklYLrqzRuLzK7/6ELcPd548BgS0OBnodcOSDaIsvNGxSMrY8= X-Received: from aliceryhl2.c.googlers.com ([fda3:e722:ac3:cc00:68:949d:c0a8:572]) (user=aliceryhl job=sendgmr) by 2002:a05:690c:98d:b0:5d3:e8b8:e1fe with SMTP id ce13-20020a05690c098d00b005d3e8b8e1femr9234ywb.0.1701864021191; Wed, 06 Dec 2023 04:00:21 -0800 (PST) Date: Wed, 06 Dec 2023 11:59:52 +0000 In-Reply-To: <20231206-alice-file-v2-0-af617c0d9d94@google.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20231206-alice-file-v2-0-af617c0d9d94@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=6796; i=aliceryhl@google.com; h=from:subject:message-id; bh=EL7Hu6623848TCiQNxfsHyxyizMwk0mqsq7s9i/TwCw=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBlcGI8P0hMAGaRy9KCnNF8/Rf30Q66IkTKoCZi8 6ZTQpLf1gGJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZXBiPAAKCRAEWL7uWMY5 Rp9/EACTkUp06YLSWQxRsvx+FQYTA7HvD9AIkv0Yw5/edduRrb6jQNOg/JSS3OcufcC+IG+gzyL +22S87vRlsgE4lHkmAuhxbFu9Wq6Slfq4maIlUKoEAJODe4u0GkvirSVb2QmIGcG58twcs0TCT/ 8kxZ6QdBlqymny0WMWvz/8ifSG3yUPQ6BHCWzZWmp2iaFMSOR2+szQQaYWATPrMA62sB7a74BjG QZTu95IzdTFkNK9x2V3625cl/Z3uK6/AUb+XTyOsVtxfgcFJj7lsjr3syJUk7lv4uhOW598Sfaj 2hgJlIckhdAMlg2CHb9i4MlfDKdxXP9XI+Jb6TbMNsVlH7UQQwli8uaQMX6vSBzjLmLYOKxh8Sj YELcYkL3nZHKB633At+X9T689xQSzmhKkigiHshCC9bbt4GnGCqUWYo+Sfgqk8BIKmkM2T9MZEA mjEwyuDR4GrvFfwqAcrlG/fqfOdVPxN+N5fbFz3NYPV9mtGnzKaJLrq+EVMm1zYaiKgitLk7rui wfIlzXX5V3r3IGg3PRN9+zQcW4RrTThXvruchNWaMyejdJDv9SEk7qUUxfV9lbUH+LeTmkKeyco xW6gRArDgBcrOeH+LDT/7+IlXPEYZ7ZLktHi0qGpW50qjVzv2E3JjY9D5YrPjTUTpzSpBRt2GjZ 6Usj49NJrkuHilA== X-Mailer: b4 0.13-dev-26615 Message-ID: <20231206-alice-file-v2-7-af617c0d9d94@google.com> Subject: [PATCH v2 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: 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, Alice Ryhl 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. 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 if we find that it is necessary to improve this, a future patch could change store a boolean next to the `wait_list` to keep track of whether a `poll_table` has ever been registered. Signed-off-by: Alice Ryhl --- rust/bindings/bindings_helper.h | 2 + rust/bindings/lib.rs | 1 + rust/kernel/sync.rs | 1 + rust/kernel/sync/poll.rs | 103 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+) 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/sync.rs b/rust/kernel/sync.rs index d219ee518eff..84726f80c406 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::CondVar; diff --git a/rust/kernel/sync/poll.rs b/rust/kernel/sync/poll.rs new file mode 100644 index 000000000000..e1dded9b7b9d --- /dev/null +++ b/rust/kernel/sync/poll.rs @@ -0,0 +1,103 @@ +// 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 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 `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.as_ptr() as _, cv.wait_list.get(), self.0.get()) }; + } + } +} + +/// A wrapper around [`CondVar`] that makes it usable with [`PollTable`]. +/// +/// # Invariant +/// +/// If `needs_synchronize_rcu` is false, then there is nothing registered with `register_wait`. +/// +/// [`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 list. + unsafe { bindings::__wake_up_pollfree(self.inner.wait_list.get()) }; + + // Wait for epoll items to be properly removed. + // + // SAFETY: Just an FFI call. + unsafe { bindings::synchronize_rcu() }; + } +}