From patchwork Mon Feb 3 12:14:43 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alice Ryhl X-Patchwork-Id: 13957353 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0EB26C02192 for ; Mon, 3 Feb 2025 12:15:39 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 97CFB280011; Mon, 3 Feb 2025 07:15:38 -0500 (EST) Received: by kanga.kvack.org (Postfix, from userid 40) id 92D84280010; Mon, 3 Feb 2025 07:15:38 -0500 (EST) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 7A717280011; Mon, 3 Feb 2025 07:15:38 -0500 (EST) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0010.hostedemail.com [216.40.44.10]) by kanga.kvack.org (Postfix) with ESMTP id 547C7280010 for ; Mon, 3 Feb 2025 07:15:38 -0500 (EST) Received: from smtpin17.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay01.hostedemail.com (Postfix) with ESMTP id 1217D1CA236 for ; Mon, 3 Feb 2025 12:15:33 +0000 (UTC) X-FDA: 83078528946.17.BC9A1D2 Received: from mail-wm1-f74.google.com (mail-wm1-f74.google.com [209.85.128.74]) by imf04.hostedemail.com (Postfix) with ESMTP id F3F784000B for ; Mon, 3 Feb 2025 12:15:30 +0000 (UTC) Authentication-Results: imf04.hostedemail.com; dkim=pass header.d=google.com header.s=20230601 header.b=XpqTDFUY; dmarc=pass (policy=reject) header.from=google.com; spf=pass (imf04.hostedemail.com: domain of 3YbOgZwkKCI4s30uw9Gz3y66y3w.u64305CF-442Dsu2.69y@flex--aliceryhl.bounces.google.com designates 209.85.128.74 as permitted sender) smtp.mailfrom=3YbOgZwkKCI4s30uw9Gz3y66y3w.u64305CF-442Dsu2.69y@flex--aliceryhl.bounces.google.com ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1738584931; a=rsa-sha256; cv=none; b=KAgKZPb4DiY9XfRA1w1AcqyaUjprZs9Gilujeb0sm+BNL1jioPP1QYgzR9pDX8VMpfJdMR V0nGVApcLrsz05ql7m343jo03CUKV7CzR20cZ6urpMFWPm69QHw/US+CE2ZBvtV5sKwBqH B14Hq7YxxbOx/UaXKPg+LMRJHyJnEIw= ARC-Authentication-Results: i=1; imf04.hostedemail.com; dkim=pass header.d=google.com header.s=20230601 header.b=XpqTDFUY; dmarc=pass (policy=reject) header.from=google.com; spf=pass (imf04.hostedemail.com: domain of 3YbOgZwkKCI4s30uw9Gz3y66y3w.u64305CF-442Dsu2.69y@flex--aliceryhl.bounces.google.com designates 209.85.128.74 as permitted sender) smtp.mailfrom=3YbOgZwkKCI4s30uw9Gz3y66y3w.u64305CF-442Dsu2.69y@flex--aliceryhl.bounces.google.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1738584931; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=tuxJJmxGW3goIRW/OgstRWONs45CMDMWq5FHoZQDXkU=; b=qdG6c/mAKTnh/pZuhl4s4gV89KuiTQZxtJzwDkNux3ANfDBT0nt+9IrZEzX97Tr3YhbEcz Zf2zOW2UvXl8T6Ll/ayfY2ttrCM2DL6qZUSlElUrF6lmEPm2ceQi4DY/zYZqf6Fo//TyZh BjGEkTfMTQUV6oEKJAg/rbvnNOKVa2c= Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-438e180821aso22305995e9.1 for ; Mon, 03 Feb 2025 04:15:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1738584929; x=1739189729; darn=kvack.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=tuxJJmxGW3goIRW/OgstRWONs45CMDMWq5FHoZQDXkU=; b=XpqTDFUYtccwmctTMwLxZ39T+LoHmDCq40sV/emejZZtK9X52irXlDwjDtxMxkieke /S6eYoCBOTGJYIdCznzZF4UmKvbhYixwjRbAaSZJ7Q4NOIJ2NDhq7rAt+l4Tv8xHkJdr sAnj7vcmnOn0ZaSSQJvpaPN2nfIjTsksoZ8gV+KNiouMjpRu9gbdoYlKvQbbGIVsy5UO s2cYY1IYGyupVEC16U7hbC+kUDEMTP8C+0ZYrKrck63M+mZtw/oUHEm8OGk0ZZr8Fp+x pQB+U8k0lxEGtS1cpdJzoLD9rVd6X9iqQX0cF6O0rM+GSAQghxyxC+yKvscdvAL8v6aG 9CkA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738584930; x=1739189730; 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=tuxJJmxGW3goIRW/OgstRWONs45CMDMWq5FHoZQDXkU=; b=K34zQ1W0l5sFmFcXKs0iz8yMOMCC0RFmcyvG9LsaxZxqo05L3X6U53l7TVyLp0rOrB Pw5DWatW2eZPNzp7mb86jotzNclvpDDUVsYPoiwSQxFVZLrUmwfLBCsunk74JqlbMlXj Xhy5GIG2/W8h6tYeHWxjpJFn9KU0qnjwDABUVoH6fvkPL6s7iyOvoZgmuDzX0wV8Ip94 bJOLUhSZMuG04+rFfMtsTc5B7/KtuaDMtEokLuaCYE/Oor8l0LnOsccXmvq9S6r7SgCC I2k6jb1JvjnyB2Ie2CgblTFnSKk+J0U3l7UcEeyDs6rS5PDP8et4VLkYNb2MungxN6d/ H3PA== X-Forwarded-Encrypted: i=1; AJvYcCVhOcOhC5JWkdYbVwt10XqDXCUg+WMPCv/ozh/SGx8KyHjT6WNdxsMIrl3hJKxONnbeXoLGqCQ6Ug==@kvack.org X-Gm-Message-State: AOJu0YyPDeYbnrZed2JLV1iS9XN3sm09ohwC8kHDF6U1GU2/okB7xyLc YlYxHZ4/AmFEO5c3X6KEv8wPY1994ge8p4BW079H2/d6FQ6t52WuNiztGmbQu94hC1ZJVFNZkOu Q81qgbgQFT1cMIA== X-Google-Smtp-Source: AGHT+IEtylH7bvRes7JMpNdtE64LFjbyAC2YtB7jplur6VQCzvF0U0vbh8ldT62ynC0a//US8WEfo3Aje7QGZ3Q= X-Received: from wmbhc24.prod.google.com ([2002:a05:600c:8718:b0:436:6fa7:621]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:4e14:b0:438:a214:52f4 with SMTP id 5b1f17b1804b1-438dc428c0amr167641675e9.25.1738584929779; Mon, 03 Feb 2025 04:15:29 -0800 (PST) Date: Mon, 03 Feb 2025 12:14:43 +0000 In-Reply-To: <20250203-vma-v13-0-2b998268a396@google.com> Mime-Version: 1.0 References: <20250203-vma-v13-0-2b998268a396@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=18134; i=aliceryhl@google.com; h=from:subject:message-id; bh=jIwDE0aeb3Nk4rOdUKVsAyevX0pk+IVA+2Vzq2nuP6o=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBnoLNNeqAtXmMLVlY0CKjjlEfSuZg8MJHG97rok y5F/UZucyKJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCZ6CzTQAKCRAEWL7uWMY5 RjDXEACFNur39ucawOYdqsVf0qKC51ENYeHIbq6oH6d1K+wE1T+QGaXNS4KRzfhdvNcT1iqIBGB 1naDOr2c79uEmPeI2jbZE/kvmTgCm7zeUvU0VvdSmq3G52t70d6xodB3iEEu3J8EM9tMLpesY/B +YWmKpSLtDxhLQbZi4iGuJLkcJCUatqUPbnl4XgXDuO1zJl/KNXa0eTvsCza17FLNbmcboLkOuB N0hMY1nX7ubbATe1SMSapf0vKX9q6Sm3PHCfQATwrA6obXVCyi0VDl7S19H/iUk6wRRuXIvpotD lA9FCajNAKrLlcSfVczW5AZU//d52G03jhLSVyjnQ4K9LQLzZe0VfBiLLqeQxckraxr8UkpAJX6 SZtg+7W5nFDOhw4Vd/yB1qBH+7ts6VHfoLyjQABIGO92T4srgZqnpjgDD9eFLScaPxNKEZbNPzU GOScRm348hc9Bl64WlCej0XqnY7HUURRIrEKnGOc2g7vschV3sgf4UMAB0bwP5GuH0SNm8r6Zca 2V4ZGRSgygTtp5T5MJ33i9mB0TxeFiSUNBurnGapesi3y0IwI/iludS9kVwXh4+31Qev2ElRMmx Ks74GDM0El+394QF6hte28v7yoJu93eCRHe2zklBMaVlLjTxf0+a96hXaPqnrs7CV80iIVLQo5E SEl0y3MRD8KGT0A== X-Mailer: b4 0.13.0 Message-ID: <20250203-vma-v13-8-2b998268a396@google.com> Subject: [PATCH v13 8/8] task: rust: rework how current is accessed From: Alice Ryhl To: Miguel Ojeda , Matthew Wilcox , Lorenzo Stoakes , Vlastimil Babka , John Hubbard , "Liam R. Howlett" , Andrew Morton , Greg Kroah-Hartman , Arnd Bergmann , Jann Horn , Suren Baghdasaryan Cc: Alex Gaynor , Boqun Feng , Gary Guo , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , Benno Lossin , Andreas Hindborg , Trevor Gross , linux-kernel@vger.kernel.org, linux-mm@kvack.org, rust-for-linux@vger.kernel.org, Alice Ryhl X-Rspamd-Server: rspam04 X-Rspamd-Queue-Id: F3F784000B X-Stat-Signature: a1n7tfcwyj99negrpcfnwatu8po7xatg X-Rspam-User: X-HE-Tag: 1738584930-421701 X-HE-Meta: U2FsdGVkX19PXS3ULqGyD4mfxvbtPFMz1nNBFuHJSSPuyvp9nZ2TQaanF4uzNiG9D8DztcrDuZWfPLQjda5U93q+thky9FzQ+f5LkvIP38AyLMTKrDuqfnTyxRwaM6Ha1ec5LE6bxIAm+cAESs9++JP5yuf+iWP8xwGbQBBgBCDp0ATUpUAWK/BdFuPdJiWft6GYWOJz0IdpTYJfhsWzW2Tb4GWAG4b5EsoKbKhLEltBCuZEzYjLGtPBUSOhlbOKv+urxviGTqATlrW2jfu6I+MRK59dAjbPFufvrDEaP0Yhyhw3nU+zxsbTKR8HBNo5PbwAyxjAWDdkbJp1LxfRhTUsdjxlZDp4rE3AXRojcfAT1SgAXUP9JtWmLS5+VxDDPUJTivWMCZETmTzSpqRkfCyJYXT7ynK5VkPXAuI8aX2mI4JwJHmypWVPh9v7A4rmN5Jqb7Gg2S4sX8Xt2DwctlfiuqERZ8Ewew3hsp/QWIOu87wNppG3qKzYzwbAVDr7Ot26fQ0Mg8dvsGrPQ+0oDlFsC4sDrAsoe6x1MMkSw0T72WEUpLWZ4IWfH365YQMScgqrGUQ0VL8u73r8fR6M0pmAa0dT+1sIyeWsurcGgJ/GkgSxlfC+vZg3m3OiZarmcYS1Xum9Mif2H/vNv8R+rUsg69at5JLglpFFQ/3YYwtdHepW1+l1d6Y7eSEKIRwbRKeAQ+CM9Gro+qOgL4azmEpGVYQeRGi5WOdD17tXDWHaedK8CNEdvYQLtO3VmL+VshC1XerHr+tqN9v0ZF5RurL5x2+diFcx7tS7Ve+Otiecrinw1aQrOC1phyZ6GLuZgbgJ1vs7vWlG60GIR8ySjKcvb82B5TCwsYbpxAFgtay4abog4PoeHSzDrPYXQtCBC/eiuEJWiDIx8JbGiuV69X5O2le4B3RkK/4jh8CNlUpAup4RYrmLbwg6tMWFsqWpNWJ34PYz8vLnZ+oQSCz 5Wl/tmCO s3o7jlMYyKgyXFWBcqbaABYILzEICGL7EAOTMBxinHdwO17zoJf/sDTpmr5lgjxFLwhHh6NRCedqgmCUT2f853wkkAHRifwn74xeks04c7KaPvEsAXprFXV7YUKvDbbIv6ywj146kTpUGq7gayCqSE+9RU/4t1pkunuWfwm16WjueLbO4Mf07G7hanYBI/9fqmI5ULjoPyOF/FNI8CvAzO8ZcdnW+M8c05BaIK3bOgjKurYrC7froDyA++ODOfgutP4p8LUb1bXq9WVNZU0rwSuE2kTUgNlLhvpqoxL6UnoKVyXres+pYEIfeP9/U+UAmJqKd/zzfMxF5fOFac8HtaFaUev5pn6Oh8swpxR5UM5YebQluxze0YBcjdXBsNs/jt2lWCUDcub3Yqd8INH7Rlx4TFi9OoIDI2lZoGw0Sj6C/u/7d4wVWAcOye3ulPGcEeGEv8LmvOIMNCwwTm0AJcNJ+jCjAp2olqJ/dSe3mblNw9gXZXwmRcNzy12BPWd2DnhE6Lmjkj3H0oL+VCKu3rqFlF9ZZJ0FUH1+mLKULvznh9nv6eK3tWUUVhOdYgrrC9baZ8c/PNA5PH0h+hOXqpxP6elOF4Ol4k1nRTKL/NYAy+O0J7Js5/0106c4IBMupHHFNWn5Tc2bhU9+SnajETd9KFDzBoEzyFwuF4vLjO5PkN7quzWf8tiVSVw== X-Bogosity: Ham, tests=bogofilter, spamicity=0.425531, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: List-Subscribe: List-Unsubscribe: Introduce a new type called `CurrentTask` that lets you perform various operations that are only safe on the `current` task. Use the new type to provide a way to access the current mm without incrementing its refcount. With this change, you can write stuff such as let vma = current!().mm().lock_vma_under_rcu(addr); without incrementing any refcounts. This replaces the existing abstractions for accessing the current pid namespace. With the old approach, every field access to current involves both a macro and a unsafe helper function. The new approach simplifies that to a single safe function on the `CurrentTask` type. This makes it less heavy-weight to add additional current accessors in the future. That said, creating a `CurrentTask` type like the one in this patch requires that we are careful to ensure that it cannot escape the current task or otherwise access things after they are freed. To do this, I declared that it cannot escape the current "task context" where I defined a "task context" as essentially the region in which `current` remains unchanged. So e.g., release_task() or begin_new_exec() would leave the task context. If a userspace thread returns to userspace and later makes another syscall, then I consider the two syscalls to be different task contexts. This allows values stored in that task to be modified between syscalls, even if they're guaranteed to be immutable during a syscall. Ensuring correctness of `CurrentTask` is slightly tricky if we also want the ability to have a safe `kthread_use_mm()` implementation in Rust. To support that safely, there are two patterns we need to ensure are safe: // Case 1: current!() called inside the scope. let mm; kthread_use_mm(some_mm, || { mm = current!().mm(); }); drop(some_mm); mm.do_something(); // UAF and: // Case 2: current!() called before the scope. let mm; let task = current!(); kthread_use_mm(some_mm, || { mm = task.mm(); }); drop(some_mm); mm.do_something(); // UAF The existing `current!()` abstraction already natively prevents the first case: The `&CurrentTask` would be tied to the inner scope, so the borrow-checker ensures that no reference derived from it can escape the scope. Fixing the second case is a bit more tricky. The solution is to essentially pretend that the contents of the scope execute on an different thread, which means that only thread-safe types can cross the boundary. Since `CurrentTask` is marked `NotThreadSafe`, attempts to move it to another thread will fail, and this includes our fake pretend thread boundary. This has the disadvantage that other types that aren't thread-safe for reasons unrelated to `current` also cannot be moved across the `kthread_use_mm()` boundary. I consider this an acceptable tradeoff. Reviewed-by: Boqun Feng Reviewed-by: Andreas Hindborg Signed-off-by: Alice Ryhl --- rust/kernel/task.rs | 247 +++++++++++++++++++++++++++------------------------- 1 file changed, 129 insertions(+), 118 deletions(-) diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index 07bc22a7645c..0b6cb9a83a2e 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -7,6 +7,7 @@ use crate::{ bindings, ffi::{c_int, c_long, c_uint}, + mm::MmWithUser, pid_namespace::PidNamespace, types::{ARef, NotThreadSafe, Opaque}, }; @@ -31,22 +32,20 @@ #[macro_export] macro_rules! current { () => { - // SAFETY: Deref + addr-of below create a temporary `TaskRef` that cannot outlive the - // caller. + // SAFETY: This expression creates a temporary value that is dropped at the end of the + // caller's scope. The following mechanisms ensure that the resulting `&CurrentTask` cannot + // leave current task context: + // + // * To return to userspace, the caller must leave the current scope. + // * Operations such as `begin_new_exec()` are necessarily unsafe and the caller of + // `begin_new_exec()` is responsible for safety. + // * Rust abstractions for things such as a `kthread_use_mm()` scope must require the + // closure to be `Send`, so the `NotThreadSafe` field of `CurrentTask` ensures that the + // `&CurrentTask` cannot cross the scope in either direction. unsafe { &*$crate::task::Task::current() } }; } -/// Returns the currently running task's pid namespace. -#[macro_export] -macro_rules! current_pid_ns { - () => { - // SAFETY: Deref + addr-of below create a temporary `PidNamespaceRef` that cannot outlive - // the caller. - unsafe { &*$crate::task::Task::current_pid_ns() } - }; -} - /// Wraps the kernel's `struct task_struct`. /// /// # Invariants @@ -85,7 +84,7 @@ macro_rules! current_pid_ns { /// impl State { /// fn new() -> Self { /// Self { -/// creator: current!().into(), +/// creator: ARef::from(&**current!()), /// index: 0, /// } /// } @@ -105,6 +104,44 @@ unsafe impl Send for Task {} // synchronised by C code (e.g., `signal_pending`). unsafe impl Sync for Task {} +/// Represents the [`Task`] in the `current` global. +/// +/// This type exists to provide more efficient operations that are only valid on the current task. +/// For example, to retrieve the pid-namespace of a task, you must use rcu protection unless it is +/// the current task. +/// +/// # Invariants +/// +/// Each value of this type must only be accessed from the task context it was created within. +/// +/// Of course, every thread is in a different task context, but for the purposes of this invariant, +/// these operations also permanently leave the task context: +/// +/// * Returning to userspace from system call context. +/// * Calling `release_task()`. +/// * Calling `begin_new_exec()` in a binary format loader. +/// +/// Other operations temporarily create a new sub-context: +/// +/// * Calling `kthread_use_mm()` creates a new context, and `kthread_unuse_mm()` returns to the +/// old context. +/// +/// This means that a `CurrentTask` obtained before a `kthread_use_mm()` call may be used again +/// once `kthread_unuse_mm()` is called, but it must not be used between these two calls. +/// Conversely, a `CurrentTask` obtained between a `kthread_use_mm()`/`kthread_unuse_mm()` pair +/// must not be used after `kthread_unuse_mm()`. +#[repr(transparent)] +pub struct CurrentTask(Task, NotThreadSafe); + +// Make all `Task` methods available on `CurrentTask`. +impl Deref for CurrentTask { + type Target = Task; + #[inline] + fn deref(&self) -> &Task { + &self.0 + } +} + /// The type of process identifiers (PIDs). type Pid = bindings::pid_t; @@ -131,119 +168,29 @@ pub fn current_raw() -> *mut bindings::task_struct { /// /// # Safety /// - /// Callers must ensure that the returned object doesn't outlive the current task/thread. - pub unsafe fn current() -> impl Deref { - struct TaskRef<'a> { - task: &'a Task, - _not_send: NotThreadSafe, + /// Callers must ensure that the returned object is only used to access a [`CurrentTask`] + /// within the task context that was active when this function was called. For more details, + /// see the invariants section for [`CurrentTask`]. + pub unsafe fn current() -> impl Deref { + struct TaskRef { + task: *const CurrentTask, } - impl Deref for TaskRef<'_> { - type Target = Task; + impl Deref for TaskRef { + type Target = CurrentTask; fn deref(&self) -> &Self::Target { - self.task + // SAFETY: The returned reference borrows from this `TaskRef`, so it cannot outlive + // the `TaskRef`, which the caller of `Task::current()` has promised will not + // outlive the task/thread for which `self.task` is the `current` pointer. Thus, it + // is okay to return a `CurrentTask` reference here. + unsafe { &*self.task } } } - let current = Task::current_raw(); TaskRef { - // SAFETY: If the current thread is still running, the current task is valid. Given - // that `TaskRef` is not `Send`, we know it cannot be transferred to another thread - // (where it could potentially outlive the caller). - task: unsafe { &*current.cast() }, - _not_send: NotThreadSafe, - } - } - - /// Returns a PidNamespace reference for the currently executing task's/thread's pid namespace. - /// - /// This function can be used to create an unbounded lifetime by e.g., storing the returned - /// PidNamespace in a global variable which would be a bug. So the recommended way to get the - /// current task's/thread's pid namespace is to use the [`current_pid_ns`] macro because it is - /// safe. - /// - /// # Safety - /// - /// Callers must ensure that the returned object doesn't outlive the current task/thread. - pub unsafe fn current_pid_ns() -> impl Deref { - struct PidNamespaceRef<'a> { - task: &'a PidNamespace, - _not_send: NotThreadSafe, - } - - impl Deref for PidNamespaceRef<'_> { - type Target = PidNamespace; - - fn deref(&self) -> &Self::Target { - self.task - } - } - - // The lifetime of `PidNamespace` is bound to `Task` and `struct pid`. - // - // The `PidNamespace` of a `Task` doesn't ever change once the `Task` is alive. A - // `unshare(CLONE_NEWPID)` or `setns(fd_pidns/pidfd, CLONE_NEWPID)` will not have an effect - // on the calling `Task`'s pid namespace. It will only effect the pid namespace of children - // created by the calling `Task`. This invariant guarantees that after having acquired a - // reference to a `Task`'s pid namespace it will remain unchanged. - // - // When a task has exited and been reaped `release_task()` will be called. This will set - // the `PidNamespace` of the task to `NULL`. So retrieving the `PidNamespace` of a task - // that is dead will return `NULL`. Note, that neither holding the RCU lock nor holding a - // referencing count to - // the `Task` will prevent `release_task()` being called. - // - // In order to retrieve the `PidNamespace` of a `Task` the `task_active_pid_ns()` function - // can be used. There are two cases to consider: - // - // (1) retrieving the `PidNamespace` of the `current` task - // (2) retrieving the `PidNamespace` of a non-`current` task - // - // From system call context retrieving the `PidNamespace` for case (1) is always safe and - // requires neither RCU locking nor a reference count to be held. Retrieving the - // `PidNamespace` after `release_task()` for current will return `NULL` but no codepath - // like that is exposed to Rust. - // - // Retrieving the `PidNamespace` from system call context for (2) requires RCU protection. - // Accessing `PidNamespace` outside of RCU protection requires a reference count that - // must've been acquired while holding the RCU lock. Note that accessing a non-`current` - // task means `NULL` can be returned as the non-`current` task could have already passed - // through `release_task()`. - // - // To retrieve (1) the `current_pid_ns!()` macro should be used which ensure that the - // returned `PidNamespace` cannot outlive the calling scope. The associated - // `current_pid_ns()` function should not be called directly as it could be abused to - // created an unbounded lifetime for `PidNamespace`. The `current_pid_ns!()` macro allows - // Rust to handle the common case of accessing `current`'s `PidNamespace` without RCU - // protection and without having to acquire a reference count. - // - // For (2) the `task_get_pid_ns()` method must be used. This will always acquire a - // reference on `PidNamespace` and will return an `Option` to force the caller to - // explicitly handle the case where `PidNamespace` is `None`, something that tends to be - // forgotten when doing the equivalent operation in `C`. Missing RCU primitives make it - // difficult to perform operations that are otherwise safe without holding a reference - // count as long as RCU protection is guaranteed. But it is not important currently. But we - // do want it in the future. - // - // Note for (2) the required RCU protection around calling `task_active_pid_ns()` - // synchronizes against putting the last reference of the associated `struct pid` of - // `task->thread_pid`. The `struct pid` stored in that field is used to retrieve the - // `PidNamespace` of the caller. When `release_task()` is called `task->thread_pid` will be - // `NULL`ed and `put_pid()` on said `struct pid` will be delayed in `free_pid()` via - // `call_rcu()` allowing everyone with an RCU protected access to the `struct pid` acquired - // from `task->thread_pid` to finish. - // - // SAFETY: The current task's pid namespace is valid as long as the current task is running. - let pidns = unsafe { bindings::task_active_pid_ns(Task::current_raw()) }; - PidNamespaceRef { - // SAFETY: If the current thread is still running, the current task and its associated - // pid namespace are valid. `PidNamespaceRef` is not `Send`, so we know it cannot be - // transferred to another thread (where it could potentially outlive the current - // `Task`). The caller needs to ensure that the PidNamespaceRef doesn't outlive the - // current task/thread. - task: unsafe { PidNamespace::from_ptr(pidns) }, - _not_send: NotThreadSafe, + // CAST: The layout of `struct task_struct` and `CurrentTask` is identical. + task: Task::current_raw().cast(), } } @@ -326,6 +273,70 @@ pub fn wake_up(&self) { } } +impl CurrentTask { + /// Access the address space of the current task. + /// + /// This function does not touch the refcount of the mm. + #[inline] + pub fn mm(&self) -> Option<&MmWithUser> { + // SAFETY: The `mm` field of `current` is not modified from other threads, so reading it is + // not a data race. + let mm = unsafe { (*self.as_ptr()).mm }; + + if mm.is_null() { + return None; + } + + // SAFETY: If `current->mm` is non-null, then it references a valid mm with a non-zero + // value of `mm_users`. Furthermore, the returned `&MmWithUser` borrows from this + // `CurrentTask`, so it cannot escape the scope in which the current pointer was obtained. + // + // This is safe even if `kthread_use_mm()`/`kthread_unuse_mm()` are used. There are two + // relevant cases: + // * If the `&CurrentTask` was created before `kthread_use_mm()`, then it cannot be + // accessed during the `kthread_use_mm()`/`kthread_unuse_mm()` scope due to the + // `NotThreadSafe` field of `CurrentTask`. + // * If the `&CurrentTask` was created within a `kthread_use_mm()`/`kthread_unuse_mm()` + // scope, then the `&CurrentTask` cannot escape that scope, so the returned `&MmWithUser` + // also cannot escape that scope. + // In either case, it's not possible to read `current->mm` and keep using it after the + // scope is ended with `kthread_unuse_mm()`. + Some(unsafe { MmWithUser::from_raw(mm) }) + } + + /// Access the pid namespace of the current task. + /// + /// This function does not touch the refcount of the namespace or use RCU protection. + /// + /// To access the pid namespace of another task, see [`Task::get_pid_ns`]. + #[doc(alias = "task_active_pid_ns")] + #[inline] + pub fn active_pid_ns(&self) -> Option<&PidNamespace> { + // SAFETY: It is safe to call `task_active_pid_ns` without RCU protection when calling it + // on the current task. + let active_ns = unsafe { bindings::task_active_pid_ns(self.as_ptr()) }; + + if active_ns.is_null() { + return None; + } + + // The lifetime of `PidNamespace` is bound to `Task` and `struct pid`. + // + // The `PidNamespace` of a `Task` doesn't ever change once the `Task` is alive. + // + // From system call context retrieving the `PidNamespace` for the current task is always + // safe and requires neither RCU locking nor a reference count to be held. Retrieving the + // `PidNamespace` after `release_task()` for current will return `NULL` but no codepath + // like that is exposed to Rust. + // + // SAFETY: If `current`'s pid ns is non-null, then it references a valid pid ns. + // Furthermore, the returned `&PidNamespace` borrows from this `CurrentTask`, so it cannot + // escape the scope in which the current pointer was obtained, e.g. it cannot live past a + // `release_task()` call. + Some(unsafe { PidNamespace::from_ptr(active_ns) }) + } +} + // SAFETY: The type invariants guarantee that `Task` is always refcounted. unsafe impl crate::types::AlwaysRefCounted for Task { fn inc_ref(&self) {