Skip to content

Commit bbdffa5

Browse files
onestackedintel-lab-lkp
authored andcommitted
rust: add UnsafePinned type
`UnsafePinned<T>` is useful for cases where a value might be shared with C code but not directly used by it. In particular this is added for storing additional data in the `MiscDeviceRegistration` which will be shared between `fops->open` and the containing struct. Similar to `Opaque` but guarantees that the value is always initialized and that the inner value is dropped when `UnsafePinned` is dropped. This was originally proposed for the IRQ abstractions [0] and is also useful for other where the inner data may be aliased, but is always valid and automatic `Drop` is desired. Since then the `UnsafePinned` type was added to upstream Rust [1] by Sky as a unstable feature, therefore this patch implements the subset of the upstream API for the `UnsafePinned` type required for additional data in `MiscDeviceRegistration` and in the implementation of the `Opaque` type. Some differences to the upstream type definition are required in the kernel implementation, because upstream type uses some compiler changes to opt out of certain optimizations, this is documented in the documentation and a comment on the `UnsafePinned` type. The documentation on is based on the upstream rust documentation with minor modifications for the kernel implementation. Link: https://lore.kernel.org/rust-for-linux/CAH5fLgiOASgjoYKFz6kWwzLaH07DqP2ph+3YyCDh2+gYqGpABA@mail.gmail.com [0] Link: rust-lang/rust#137043 [1] Suggested-by: Alice Ryhl <[email protected]> Reviewed-by: Gerald Wisböck <[email protected]> Reviewed-by: Alice Ryhl <[email protected]> Co-developed-by: Sky <[email protected]> Signed-off-by: Sky <[email protected]> Signed-off-by: Christian Schrefl <[email protected]>
1 parent 9de1a29 commit bbdffa5

File tree

2 files changed

+119
-0
lines changed

2 files changed

+119
-0
lines changed

rust/kernel/types.rs

+6
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ impl<T, F: FnOnce(T)> Drop for ScopeGuard<T, F> {
253253
///
254254
/// [`Opaque<T>`] is meant to be used with FFI objects that are never interpreted by Rust code.
255255
///
256+
/// In cases where the contained data is only used by Rust, is not allowed to be
257+
/// uninitialized and automatic [`Drop`] is desired [`UnsafePinned`] should be used instead.
258+
///
256259
/// It is used to wrap structs from the C side, like for example `Opaque<bindings::mutex>`.
257260
/// It gets rid of all the usual assumptions that Rust has for a value:
258261
///
@@ -578,3 +581,6 @@ pub type NotThreadSafe = PhantomData<*mut ()>;
578581
/// [`NotThreadSafe`]: type@NotThreadSafe
579582
#[allow(non_upper_case_globals)]
580583
pub const NotThreadSafe: NotThreadSafe = PhantomData;
584+
585+
mod unsafe_pinned;
586+
pub use unsafe_pinned::UnsafePinned;

rust/kernel/types/unsafe_pinned.rs

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
3+
//! The contents of this file partially come from the Rust standard library, hosted in
4+
//! the <https://github.com/rust-lang/rust> repository, licensed under
5+
//! "Apache-2.0 OR MIT" and adapted for kernel use. For copyright details,
6+
//! see <https://github.com/rust-lang/rust/blob/master/COPYRIGHT>.
7+
//!
8+
//! This file provides a implementation / polyfill of a subset of the upstream
9+
//! rust `UnsafePinned` type. For details on the difference to the upstream
10+
//! implementation see the comment on the [`UnsafePinned`] struct definition.
11+
12+
use core::{cell::UnsafeCell, marker::PhantomPinned};
13+
use pin_init::{cast_pin_init, PinInit, Wrapper};
14+
15+
/// This type provides a way to opt-out of typical aliasing rules;
16+
/// specifically, `&mut UnsafePinned<T>` is not guaranteed to be a unique pointer.
17+
///
18+
/// However, even if you define your type like `pub struct Wrapper(UnsafePinned<...>)`, it is still
19+
/// very risky to have an `&mut Wrapper` that aliases anything else. Many functions that work
20+
/// generically on `&mut T` assume that the memory that stores `T` is uniquely owned (such as
21+
/// `mem::swap`). In other words, while having aliasing with `&mut Wrapper` is not immediate
22+
/// Undefined Behavior, it is still unsound to expose such a mutable reference to code you do not
23+
/// control! Techniques such as pinning via [`Pin`](core::pin::Pin) are needed to ensure soundness.
24+
///
25+
/// Similar to [`UnsafeCell`], [`UnsafePinned`] will not usually show up in
26+
/// the public API of a library. It is an internal implementation detail of libraries that need to
27+
/// support aliasing mutable references.
28+
///
29+
/// Further note that this does *not* lift the requirement that shared references must be read-only!
30+
/// Use [`UnsafeCell`] for that.
31+
///
32+
/// This type blocks niches the same way [`UnsafeCell`] does.
33+
///
34+
/// # Kernel implementation notes
35+
///
36+
/// This implementation works because of the "`!Unpin` hack" in rustc, which allows (some kinds of)
37+
/// mutual aliasing of `!Unpin` types. This hack might be removed at some point, after which only
38+
/// the `core::pin::UnsafePinned` type will allow this behavior. In order to simplify the migration
39+
/// to future rust versions only this polyfill of this type should be used when this behavior is
40+
/// required.
41+
//
42+
// As opposed to the upstream Rust type this contains a `PhantomPinned` and `UnsafeCell<T>`
43+
// - `PhantomPinned` to ensure the struct always is `!Unpin` and thus enables the `!Unpin` hack.
44+
// This causes the LLVM `noalias` and `dereferenceable` attributes to be removed from
45+
// `&mut !Unpin` types.
46+
// - In order to disable niche optimizations this implementation uses `UnsafeCell` internally,
47+
// the upstream version however currently does not. This will most likely change in the future
48+
// but for now we don't expose this in the documentation, since adding the guarantee is simpler
49+
// than removing it. Meaning that for now the fact that `UnsafePinned` contains an `UnsafeCell`
50+
// must not be relied on (Other than the niche blocking).
51+
// See this Rust tracking issue: https://github.com/rust-lang/rust/issues/137750
52+
#[repr(transparent)]
53+
pub struct UnsafePinned<T: ?Sized> {
54+
_ph: PhantomPinned,
55+
value: UnsafeCell<T>,
56+
}
57+
58+
impl<T> UnsafePinned<T> {
59+
/// Constructs a new instance of [`UnsafePinned`] which will wrap the specified value.
60+
///
61+
/// All access to the inner value through `&UnsafePinned<T>` or `&mut UnsafePinned<T>` or
62+
/// `Pin<&mut UnsafePinned<T>>` requires `unsafe` code.
63+
#[inline(always)]
64+
#[must_use]
65+
pub const fn new(value: T) -> Self {
66+
UnsafePinned {
67+
value: UnsafeCell::new(value),
68+
_ph: PhantomPinned,
69+
}
70+
}
71+
}
72+
impl<T: ?Sized> UnsafePinned<T> {
73+
/// Get read-only access to the contents of a shared `UnsafePinned`.
74+
///
75+
/// Note that `&UnsafePinned<T>` is read-only if `&T` is read-only. This means that if there is
76+
/// mutation of the `T`, future reads from the `*const T` returned here are UB! Use
77+
/// [`UnsafeCell`] if you also need interior mutability.
78+
///
79+
/// [`UnsafeCell`]: core::cell::UnsafeCell
80+
///
81+
/// ```rust,no_build
82+
/// use kernel::types::UnsafePinned;
83+
///
84+
/// unsafe {
85+
/// let mut x = UnsafePinned::new(0);
86+
/// let ptr = x.get(); // read-only pointer, assumes immutability
87+
/// x.get_mut_unchecked().write(1);
88+
/// ptr.read(); // UB!
89+
/// }
90+
/// ```
91+
///
92+
/// Note that the `get_mut_unchecked` function used by this example is
93+
/// currently not implemented in the kernel implementation.
94+
#[inline(always)]
95+
#[must_use]
96+
pub const fn get(&self) -> *const T {
97+
self.value.get()
98+
}
99+
100+
/// Gets a mutable pointer to the wrapped value.
101+
#[inline(always)]
102+
#[must_use]
103+
pub const fn raw_get_mut(this: *mut Self) -> *mut T {
104+
this as *mut T
105+
}
106+
}
107+
108+
impl<T> Wrapper<T> for UnsafePinned<T> {
109+
fn pin_init<E>(init: impl PinInit<T, E>) -> impl PinInit<Self, E> {
110+
// SAFETY: `UnsafePinned<T>` has a compatible layout to `T`.
111+
unsafe { cast_pin_init(init) }
112+
}
113+
}

0 commit comments

Comments
 (0)