From 1b8c8bd0db3ed11e29034b8d3807d4f082483987 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 10 Jul 2023 07:46:42 +0000 Subject: [PATCH 01/18] rust: add improved version of `ForeignOwnable::borrow_mut` Previously, the `ForeignOwnable` trait had a method called `borrow_mut` that was intended to provide mutable access to the inner value. However, the method accidentally made it possible to change the address of the object being modified, which usually isn't what we want. (And when we want that, it can be done by calling `from_foreign` and `into_foreign`, like how the old `borrow_mut` was implemented.) In this patch, we introduce an alternate definition of `borrow_mut` that solves the previous problem. Conceptually, given a pointer type `P` that implements `ForeignOwnable`, the `borrow_mut` method gives you the same kind of access as an `&mut P` would, except that it does not let you change the pointer `P` itself. This is analogous to how the existing `borrow` method provides the same kind of access to the inner value as an `&P`. Note that for types like `Arc`, having an `&mut Arc` only gives you immutable access to the inner `T`. This is because mutable references assume exclusive access, but there might be other handles to the same reference counted value, so the access isn't exclusive. The `Arc` type implements this by making `borrow_mut` return the same type as `borrow`. Signed-off-by: Alice Ryhl Reviewed-by: Benno Lossin Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Boqun Feng Link: https://lore.kernel.org/r/20230710074642.683831-1-aliceryhl@google.com --- rust/kernel/sync/arc.rs | 25 ++++++++---- rust/kernel/types.rs | 87 +++++++++++++++++++++++++++++++---------- 2 files changed, 83 insertions(+), 29 deletions(-) diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index 77cdbcf7bd2eba..97e7146a7b1fa0 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -277,26 +277,35 @@ impl Arc { impl ForeignOwnable for Arc { type Borrowed<'a> = ArcBorrow<'a, T>; + // Mutable access to the `Arc` does not give any extra abilities over + // immutable access. + type BorrowedMut<'a> = ArcBorrow<'a, T>; fn into_foreign(self) -> *const core::ffi::c_void { ManuallyDrop::new(self).ptr.as_ptr() as _ } + unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self { + // SAFETY: By the safety requirement of this function, we know that `ptr` came from + // a previous call to `Arc::into_foreign`, which guarantees that `ptr` is valid and + // holds a reference count increment that is transferrable to us. + unsafe { Self::from_inner(NonNull::new_unchecked(ptr as _)) } + } + unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> ArcBorrow<'a, T> { // SAFETY: By the safety requirement of this function, we know that `ptr` came from // a previous call to `Arc::into_foreign`. - let inner = NonNull::new(ptr as *mut ArcInner).unwrap(); + let inner = unsafe { NonNull::new_unchecked(ptr as *mut ArcInner) }; - // SAFETY: The safety requirements of `from_foreign` ensure that the object remains alive - // for the lifetime of the returned value. + // SAFETY: The safety requirements ensure that we will not give up our + // foreign-owned refcount while the `ArcBorrow` is still live. unsafe { ArcBorrow::new(inner) } } - unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self { - // SAFETY: By the safety requirement of this function, we know that `ptr` came from - // a previous call to `Arc::into_foreign`, which guarantees that `ptr` is valid and - // holds a reference count increment that is transferrable to us. - unsafe { Self::from_inner(NonNull::new(ptr as _).unwrap()) } + unsafe fn borrow_mut<'a>(ptr: *const core::ffi::c_void) -> ArcBorrow<'a, T> { + // SAFETY: The safety requirements for `borrow_mut` are a superset of the safety + // requirements for `borrow`. + unsafe { Self::borrow(ptr) } } } diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index fdb778e65d79d3..d849e1979ac707 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -20,66 +20,111 @@ use core::{ /// This trait is meant to be used in cases when Rust objects are stored in C objects and /// eventually "freed" back to Rust. pub trait ForeignOwnable: Sized { - /// Type of values borrowed between calls to [`ForeignOwnable::into_foreign`] and - /// [`ForeignOwnable::from_foreign`]. + /// Type used to immutably borrow a value that is currently foreign-owned. type Borrowed<'a>; + /// Type used to mutably borrow a value that is currently foreign-owned. + type BorrowedMut<'a>; + /// Converts a Rust-owned object to a foreign-owned one. /// /// The foreign representation is a pointer to void. fn into_foreign(self) -> *const core::ffi::c_void; - /// Borrows a foreign-owned object. + /// Converts a foreign-owned object back to a Rust-owned one. + /// + /// # Safety + /// + /// The provided pointer must have been returned by a previous call to [`into_foreign`], and it + /// must not be passed to `from_foreign` more than once. + /// + /// [`into_foreign`]: Self::into_foreign + unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self; + + /// Borrows a foreign-owned object immutably. + /// + /// This method provides a way to access a foreign-owned value from Rust immutably. It provides + /// you with exactly the same abilities as an `&Self` when the value is Rust-owned. /// /// # Safety /// - /// `ptr` must have been returned by a previous call to [`ForeignOwnable::into_foreign`] for - /// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet. + /// The provided pointer must have been returned by a previous call to [`into_foreign`], and if + /// the pointer is ever passed to [`from_foreign`], then that call must happen after the end of + /// the lifetime 'a. + /// + /// [`into_foreign`]: Self::into_foreign + /// [`from_foreign`]: Self::from_foreign unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Self::Borrowed<'a>; - /// Converts a foreign-owned object back to a Rust-owned one. + /// Borrows a foreign-owned object mutably. + /// + /// This method provides a way to access a foreign-owned value from Rust mutably. It provides + /// you with exactly the same abilities as an `&mut Self` when the value is Rust-owned, except + /// that this method does not let you swap the foreign-owned object for another. (That is, it + /// does not let you change the address of the void pointer that the foreign code is storing.) + /// + /// Note that for types like [`Arc`], an `&mut Arc` only gives you immutable access to the + /// inner value, so this method also only provides immutable access in that case. + /// + /// In the case of `Box`, this method gives you the ability to modify the inner `T`, but it + /// does not let you change the box itself. That is, you cannot change which allocation the box + /// points at. /// /// # Safety /// - /// `ptr` must have been returned by a previous call to [`ForeignOwnable::into_foreign`] for - /// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet. - /// Additionally, all instances (if any) of values returned by [`ForeignOwnable::borrow`] for - /// this object must have been dropped. - unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self; + /// The provided pointer must have been returned by a previous call to [`into_foreign`], and if + /// the pointer is ever passed to [`from_foreign`], then that call must happen after the end of + /// the lifetime 'a. + /// + /// The lifetime 'a must not overlap with the lifetime of any other call to [`borrow`] or + /// `borrow_mut` on the same object. + /// + /// [`into_foreign`]: Self::into_foreign + /// [`from_foreign`]: Self::from_foreign + /// [`borrow`]: Self::borrow + /// [`Arc`]: crate::sync::Arc + unsafe fn borrow_mut<'a>(ptr: *const core::ffi::c_void) -> Self::BorrowedMut<'a>; } impl ForeignOwnable for Box { type Borrowed<'a> = &'a T; + type BorrowedMut<'a> = &'a mut T; fn into_foreign(self) -> *const core::ffi::c_void { Box::into_raw(self) as _ } - unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'a T { - // SAFETY: The safety requirements for this function ensure that the object is still alive, - // so it is safe to dereference the raw pointer. - // The safety requirements of `from_foreign` also ensure that the object remains alive for - // the lifetime of the returned value. - unsafe { &*ptr.cast() } - } - unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self { // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous // call to `Self::into_foreign`. unsafe { Box::from_raw(ptr as _) } } + + unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'a T { + // SAFETY: The safety requirements of this method ensure that the object remains alive and + // immutable for the duration of 'a. + unsafe { &*ptr.cast() } + } + + unsafe fn borrow_mut<'a>(ptr: *const core::ffi::c_void) -> &'a mut T { + // SAFETY: The safety requirements of this method ensure that the pointer is valid and that + // nothing else will access the value for the duration of 'a. + unsafe { &mut *ptr.cast_mut().cast() } + } } impl ForeignOwnable for () { type Borrowed<'a> = (); + type BorrowedMut<'a> = (); fn into_foreign(self) -> *const core::ffi::c_void { core::ptr::NonNull::dangling().as_ptr() } - unsafe fn borrow<'a>(_: *const core::ffi::c_void) -> Self::Borrowed<'a> {} - unsafe fn from_foreign(_: *const core::ffi::c_void) -> Self {} + + unsafe fn borrow<'a>(_: *const core::ffi::c_void) -> Self::Borrowed<'a> {} + unsafe fn borrow_mut<'a>(_: *const core::ffi::c_void) -> Self::BorrowedMut<'a> {} } /// Runs a cleanup function/closure when dropped. From 9c7c6e9f308912b81c9988feaa689b24a461b75f Mon Sep 17 00:00:00 2001 From: Jamie Cunliffe Date: Fri, 20 Oct 2023 16:50:55 +0100 Subject: [PATCH 02/18] rust: Refactor the build target to allow the use of builtin targets Eventually we want all architectures to be using the target as defined by rustc. However currently some architectures can't do that and are using the target.json specification. This puts in place the foundation to allow the use of the builtin target definition or a target.json specification. Signed-off-by: Jamie Cunliffe Link: https://lore.kernel.org/r/20231020155056.3495121-2-Jamie.Cunliffe@arm.com --- Makefile | 1 - arch/x86/Makefile | 1 + rust/Makefile | 5 ++++- scripts/Makefile | 4 +++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 70fc4c11dfc04e..b5b57bd99a3ff6 100644 --- a/Makefile +++ b/Makefile @@ -555,7 +555,6 @@ KBUILD_CFLAGS += -fno-strict-aliasing KBUILD_CPPFLAGS := -D__KERNEL__ KBUILD_RUSTFLAGS := $(rust_common_flags) \ - --target=$(objtree)/scripts/target.json \ -Cpanic=abort -Cembed-bitcode=n -Clto=n \ -Cforce-unwind-tables=n -Ccodegen-units=1 \ -Csymbol-mangling-version=v0 \ diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 1a068de12a564f..86d347ff6b3362 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -68,6 +68,7 @@ export BITS # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53383 # KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx +KBUILD_RUSTFLAGS += --target=$(objtree)/scripts/target.json KBUILD_RUSTFLAGS += -Ctarget-feature=-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2 ifeq ($(CONFIG_X86_KERNEL_IBT),y) diff --git a/rust/Makefile b/rust/Makefile index 5a3e3140e23486..0c7ba3c95116f4 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -433,8 +433,11 @@ $(obj)/core.o: private skip_clippy = 1 $(obj)/core.o: private skip_flags = -Dunreachable_pub $(obj)/core.o: private rustc_objcopy = $(foreach sym,$(redirect-intrinsics),--redefine-sym $(sym)=__rust$(sym)) $(obj)/core.o: private rustc_target_flags = $(core-cfgs) -$(obj)/core.o: $(RUST_LIB_SRC)/core/src/lib.rs scripts/target.json FORCE +$(obj)/core.o: $(RUST_LIB_SRC)/core/src/lib.rs FORCE $(call if_changed_dep,rustc_library) +ifdef CONFIG_X86_64 +$(obj)/core.o: scripts/target.json +endif $(obj)/compiler_builtins.o: private rustc_objcopy = -w -W '__*' $(obj)/compiler_builtins.o: $(src)/compiler_builtins.rs $(obj)/core.o FORCE diff --git a/scripts/Makefile b/scripts/Makefile index 576cf64be6677c..c85d130a4125bd 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -11,12 +11,14 @@ hostprogs-always-$(CONFIG_MODULE_SIG_FORMAT) += sign-file hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_builder hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_gen -always-$(CONFIG_RUST) += target.json +ifdef CONFIG_X86_64 +always-$(CONFIG_RUST) += target.json filechk_rust_target = $< < include/config/auto.conf $(obj)/target.json: scripts/generate_rust_target include/config/auto.conf FORCE $(call filechk,rust_target) +endif hostprogs += generate_rust_target generate_rust_target-rust := y From 624d445e61fd2c2d9d12ff1374a4ebfd3094f27e Mon Sep 17 00:00:00 2001 From: Jamie Cunliffe Date: Fri, 20 Oct 2023 16:50:56 +0100 Subject: [PATCH 03/18] arm64: rust: Enable Rust support for AArch64 This commit provides the build flags for Rust for AArch64. The core Rust support already in the kernel does the rest. This enables the PAC ret and BTI options in the Rust build flags to match the options that are used when building C. The Rust samples have been tested with this commit. Signed-off-by: Jamie Cunliffe Acked-by: Will Deacon Acked-by: Catalin Marinas Link: https://lore.kernel.org/r/20231020155056.3495121-3-Jamie.Cunliffe@arm.com --- Documentation/rust/arch-support.rst | 1 + arch/arm64/Kconfig | 1 + arch/arm64/Makefile | 4 ++++ rust/Makefile | 1 + scripts/generate_rust_target.rs | 4 +++- 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Documentation/rust/arch-support.rst b/Documentation/rust/arch-support.rst index b91e9ef4d0c21e..6bcb3b97c5b610 100644 --- a/Documentation/rust/arch-support.rst +++ b/Documentation/rust/arch-support.rst @@ -15,6 +15,7 @@ support corresponds to ``S`` values in the ``MAINTAINERS`` file. ============ ================ ============================================== Architecture Level of support Constraints ============ ================ ============================================== +``arm64`` Maintained Little Endian only. ``um`` Maintained ``x86_64`` only. ``x86`` Maintained ``x86_64`` only. ============ ================ ============================================== diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 7b071a00425d2b..1aaa1d456d9d4b 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -227,6 +227,7 @@ config ARM64 select HAVE_FUNCTION_ARG_ACCESS_API select MMU_GATHER_RCU_TABLE_FREE select HAVE_RSEQ + select HAVE_RUST if CPU_LITTLE_ENDIAN select HAVE_STACKPROTECTOR select HAVE_SYSCALL_TRACEPOINTS select HAVE_KPROBES diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 9a2d3723cd0fa9..7af127552f5302 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -41,6 +41,8 @@ KBUILD_CFLAGS += -mgeneral-regs-only \ KBUILD_CFLAGS += $(call cc-disable-warning, psabi) KBUILD_AFLAGS += $(compat_vdso) +KBUILD_RUSTFLAGS += --target=aarch64-unknown-none -Ctarget-feature="-neon" + KBUILD_CFLAGS += $(call cc-option,-mabi=lp64) KBUILD_AFLAGS += $(call cc-option,-mabi=lp64) @@ -65,7 +67,9 @@ endif ifeq ($(CONFIG_ARM64_BTI_KERNEL),y) KBUILD_CFLAGS += -mbranch-protection=pac-ret+bti + KBUILD_RUSTFLAGS += -Zbranch-protection=bti,pac-ret else ifeq ($(CONFIG_ARM64_PTR_AUTH_KERNEL),y) + KBUILD_RUSTFLAGS += -Zbranch-protection=pac-ret ifeq ($(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET),y) KBUILD_CFLAGS += -mbranch-protection=pac-ret else diff --git a/rust/Makefile b/rust/Makefile index 0c7ba3c95116f4..c5fbbd97d4f2d0 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -296,6 +296,7 @@ bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \ # Derived from `scripts/Makefile.clang`. BINDGEN_TARGET_x86 := x86_64-linux-gnu +BINDGEN_TARGET_arm64 := aarch64-linux-gnu BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH)) # All warnings are inhibited since GCC builds are very experimental, diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs index 3c6cbe2b278d30..ec5ef35dbe52db 100644 --- a/scripts/generate_rust_target.rs +++ b/scripts/generate_rust_target.rs @@ -148,7 +148,9 @@ fn main() { let mut ts = TargetSpec::new(); // `llvm-target`s are taken from `scripts/Makefile.clang`. - if cfg.has("X86_64") { + if cfg.has("ARM64") { + panic!("arm64 uses the builtin rustc aarch64-unknown-none target"); + } else if cfg.has("X86_64") { ts.push("arch", "x86_64"); ts.push( "data-layout", From 9ba4ccc161ec54deca367e445286b00402930c67 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Thu, 14 Dec 2023 10:29:58 +0100 Subject: [PATCH 04/18] rust: upgrade to Rust 1.74.1 This is the next upgrade to the Rust toolchain, from 1.73.0 to 1.74.1 (i.e. the latest) [1]. See the upgrade policy [2] and the comments on the first upgrade in commit 3ed03f4da06e ("rust: upgrade to Rust 1.68.2"). # Unstable features No unstable features (that we use) were stabilized. Therefore, the only unstable features allowed to be used outside the `kernel` crate are still `new_uninit,offset_of`, though other code to be upstreamed may increase the list (e.g. `offset_of` was added recently). Please see [3] for details. # Other improvements Rust 1.74.0 allows to use `#[repr(Rust)]` explicitly [4], which can be useful to be explicit about particular cases that would normally use e.g. the C representation, such as silencing lints like the upcoming additions we requested [5] to the `no_mangle_with_rust_abi` Clippy lint (which in turn triggered the `#[repr(Rust)]` addition). Rust 1.74.0 includes a fix for one of the false negative cases we reported in Clippy's `disallowed_macros` lint [6] that we would like to use in the future. Rust 1.74.1 fixes an ICE that the Apple AGX GPU driver was hitting [7]. # Required changes For this upgrade, no changes were required (i.e. on our side). # `alloc` upgrade and reviewing The vast majority of changes are due to our `alloc` fork being upgraded at once. There are two kinds of changes to be aware of: the ones coming from upstream, which we should follow as closely as possible, and the updates needed in our added fallible APIs to keep them matching the newer infallible APIs coming from upstream. Instead of taking a look at the diff of this patch, an alternative approach is reviewing a diff of the changes between upstream `alloc` and the kernel's. This allows to easily inspect the kernel additions only, especially to check if the fallible methods we already have still match the infallible ones in the new version coming from upstream. Another approach is reviewing the changes introduced in the additions in the kernel fork between the two versions. This is useful to spot potentially unintended changes to our additions. To apply these approaches, one may follow steps similar to the following to generate a pair of patches that show the differences between upstream Rust and the kernel (for the subset of `alloc` we use) before and after applying this patch: # Get the difference with respect to the old version. git -C rust checkout $(linux/scripts/min-tool-version.sh rustc) git -C linux ls-tree -r --name-only HEAD -- rust/alloc | cut -d/ -f3- | grep -Fv README.md | xargs -IPATH cp rust/library/alloc/src/PATH linux/rust/alloc/PATH git -C linux diff --patch-with-stat --summary -R > old.patch git -C linux restore rust/alloc # Apply this patch. git -C linux am rust-upgrade.patch # Get the difference with respect to the new version. git -C rust checkout $(linux/scripts/min-tool-version.sh rustc) git -C linux ls-tree -r --name-only HEAD -- rust/alloc | cut -d/ -f3- | grep -Fv README.md | xargs -IPATH cp rust/library/alloc/src/PATH linux/rust/alloc/PATH git -C linux diff --patch-with-stat --summary -R > new.patch git -C linux restore rust/alloc Now one may check the `new.patch` to take a look at the additions (first approach) or at the difference between those two patches (second approach). For the latter, a side-by-side tool is recommended. Link: https://github.com/rust-lang/rust/blob/stable/RELEASES.md#version-1741-2023-12-07 [1] Link: https://rust-for-linux.com/rust-version-policy [2] Link: https://github.com/Rust-for-Linux/linux/issues/2 [3] Link: https://github.com/rust-lang/rust/pull/114201 [4] Link: https://github.com/rust-lang/rust-clippy/issues/11219 [5] Link: https://github.com/rust-lang/rust-clippy/issues/11431 [6] Link: https://github.com/rust-lang/rust/issues/117976#issuecomment-1822225691 [7] Signed-off-by: Miguel Ojeda Reviewed-by: Alice Ryhl Reviewed-by: Martin Rodriguez Reboredo Link: https://lore.kernel.org/r/20231214092958.377061-1-ojeda@kernel.org --- Documentation/process/changes.rst | 2 +- rust/alloc/alloc.rs | 32 ++++++++---- rust/alloc/lib.rs | 6 +-- rust/alloc/slice.rs | 2 +- rust/alloc/vec/mod.rs | 87 ++++++++++++++++++++++++++++++- scripts/min-tool-version.sh | 2 +- 6 files changed, 114 insertions(+), 17 deletions(-) diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst index bb96ca0f774b9a..169f67773518ca 100644 --- a/Documentation/process/changes.rst +++ b/Documentation/process/changes.rst @@ -31,7 +31,7 @@ you probably needn't concern yourself with pcmciautils. ====================== =============== ======================================== GNU C 5.1 gcc --version Clang/LLVM (optional) 11.0.0 clang --version -Rust (optional) 1.73.0 rustc --version +Rust (optional) 1.74.1 rustc --version bindgen (optional) 0.65.1 bindgen --version GNU make 3.82 make --version bash 4.2 bash --version diff --git a/rust/alloc/alloc.rs b/rust/alloc/alloc.rs index 8cb4a31cf6e54d..150e13750ff70e 100644 --- a/rust/alloc/alloc.rs +++ b/rust/alloc/alloc.rs @@ -345,18 +345,31 @@ extern "Rust" { fn __rust_alloc_error_handler(size: usize, align: usize) -> !; } -/// Abort on memory allocation error or failure. +/// Signal a memory allocation error. /// -/// Callers of memory allocation APIs wishing to abort computation +/// Callers of memory allocation APIs wishing to cease execution /// in response to an allocation error are encouraged to call this function, -/// rather than directly invoking `panic!` or similar. +/// rather than directly invoking [`panic!`] or similar. /// -/// The default behavior of this function is to print a message to standard error -/// and abort the process. -/// It can be replaced with [`set_alloc_error_hook`] and [`take_alloc_error_hook`]. +/// This function is guaranteed to diverge (not return normally with a value), but depending on +/// global configuration, it may either panic (resulting in unwinding or aborting as per +/// configuration for all panics), or abort the process (with no unwinding). +/// +/// The default behavior is: +/// +/// * If the binary links against `std` (typically the case), then +/// print a message to standard error and abort the process. +/// This behavior can be replaced with [`set_alloc_error_hook`] and [`take_alloc_error_hook`]. +/// Future versions of Rust may panic by default instead. +/// +/// * If the binary does not link against `std` (all of its crates are marked +/// [`#![no_std]`][no_std]), then call [`panic!`] with a message. +/// [The panic handler] applies as to any panic. /// /// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html /// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html +/// [The panic handler]: https://doc.rust-lang.org/reference/runtime.html#the-panic_handler-attribute +/// [no_std]: https://doc.rust-lang.org/reference/names/preludes.html#the-no_std-attribute #[stable(feature = "global_alloc", since = "1.28.0")] #[rustc_const_unstable(feature = "const_alloc_error", issue = "92523")] #[cfg(all(not(no_global_oom_handling), not(test)))] @@ -397,9 +410,10 @@ pub mod __alloc_error_handler { if unsafe { __rust_alloc_error_handler_should_panic != 0 } { panic!("memory allocation of {size} bytes failed") } else { - core::panicking::panic_nounwind_fmt(format_args!( - "memory allocation of {size} bytes failed" - )) + core::panicking::panic_nounwind_fmt( + format_args!("memory allocation of {size} bytes failed"), + /* force_no_backtrace */ false, + ) } } } diff --git a/rust/alloc/lib.rs b/rust/alloc/lib.rs index 73b9ffd845d952..9c7ea73da10800 100644 --- a/rust/alloc/lib.rs +++ b/rust/alloc/lib.rs @@ -90,8 +90,8 @@ #![warn(missing_docs)] #![allow(explicit_outlives_requirements)] #![warn(multiple_supertrait_upcastable)] -#![cfg_attr(not(bootstrap), allow(internal_features))] -#![cfg_attr(not(bootstrap), allow(rustdoc::redundant_explicit_links))] +#![allow(internal_features)] +#![allow(rustdoc::redundant_explicit_links)] // // Library features: // tidy-alphabetical-start @@ -122,6 +122,7 @@ #![feature(const_waker)] #![feature(core_intrinsics)] #![feature(core_panic)] +#![feature(deprecated_suggestion)] #![feature(dispatch_from_dyn)] #![feature(error_generic_member_access)] #![feature(error_in_core)] @@ -145,7 +146,6 @@ #![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] #![feature(receiver_trait)] -#![feature(saturating_int_impl)] #![feature(set_ptr_value)] #![feature(sized_type_properties)] #![feature(slice_from_ptr_range)] diff --git a/rust/alloc/slice.rs b/rust/alloc/slice.rs index 6ac463bd3edc53..1181836da5f462 100644 --- a/rust/alloc/slice.rs +++ b/rust/alloc/slice.rs @@ -594,7 +594,7 @@ impl [T] { /// ``` #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] - #[deprecated(since = "1.3.0", note = "renamed to join")] + #[deprecated(since = "1.3.0", note = "renamed to join", suggestion = "join")] pub fn connect(&self, sep: Separator) -> >::Output where Self: Join, diff --git a/rust/alloc/vec/mod.rs b/rust/alloc/vec/mod.rs index 209a88cfe598f1..41ca71805ef09e 100644 --- a/rust/alloc/vec/mod.rs +++ b/rust/alloc/vec/mod.rs @@ -1228,8 +1228,8 @@ impl Vec { /// Shortens the vector, keeping the first `len` elements and dropping /// the rest. /// - /// If `len` is greater than the vector's current length, this has no - /// effect. + /// If `len` is greater or equal to the vector's current length, this has + /// no effect. /// /// The [`drain`] method can emulate `truncate`, but causes the excess /// elements to be returned instead of dropped. @@ -1336,6 +1336,15 @@ impl Vec { /// is never written to (except inside an `UnsafeCell`) using this pointer or any pointer /// derived from it. If you need to mutate the contents of the slice, use [`as_mut_ptr`]. /// + /// This method guarantees that for the purpose of the aliasing model, this method + /// does not materialize a reference to the underlying slice, and thus the returned pointer + /// will remain valid when mixed with other calls to [`as_ptr`] and [`as_mut_ptr`]. + /// Note that calling other methods that materialize mutable references to the slice, + /// or mutable references to specific elements you are planning on accessing through this pointer, + /// as well as writing to those elements, may still invalidate this pointer. + /// See the second example below for how this guarantee can be used. + /// + /// /// # Examples /// /// ``` @@ -1349,8 +1358,25 @@ impl Vec { /// } /// ``` /// + /// Due to the aliasing guarantee, the following code is legal: + /// + /// ```rust + /// unsafe { + /// let mut v = vec![0, 1, 2]; + /// let ptr1 = v.as_ptr(); + /// let _ = ptr1.read(); + /// let ptr2 = v.as_mut_ptr().offset(2); + /// ptr2.write(2); + /// // Notably, the write to `ptr2` did *not* invalidate `ptr1` + /// // because it mutated a different element: + /// let _ = ptr1.read(); + /// } + /// ``` + /// /// [`as_mut_ptr`]: Vec::as_mut_ptr + /// [`as_ptr`]: Vec::as_ptr #[stable(feature = "vec_as_ptr", since = "1.37.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] #[inline] pub fn as_ptr(&self) -> *const T { // We shadow the slice method of the same name to avoid going through @@ -1366,6 +1392,15 @@ impl Vec { /// Modifying the vector may cause its buffer to be reallocated, /// which would also make any pointers to it invalid. /// + /// This method guarantees that for the purpose of the aliasing model, this method + /// does not materialize a reference to the underlying slice, and thus the returned pointer + /// will remain valid when mixed with other calls to [`as_ptr`] and [`as_mut_ptr`]. + /// Note that calling other methods that materialize references to the slice, + /// or references to specific elements you are planning on accessing through this pointer, + /// may still invalidate this pointer. + /// See the second example below for how this guarantee can be used. + /// + /// /// # Examples /// /// ``` @@ -1383,7 +1418,25 @@ impl Vec { /// } /// assert_eq!(&*x, &[0, 1, 2, 3]); /// ``` + /// + /// Due to the aliasing guarantee, the following code is legal: + /// + /// ```rust + /// unsafe { + /// let mut v = vec![0]; + /// let ptr1 = v.as_mut_ptr(); + /// ptr1.write(1); + /// let ptr2 = v.as_mut_ptr(); + /// ptr2.write(2); + /// // Notably, the write to `ptr2` did *not* invalidate `ptr1`: + /// ptr1.write(3); + /// } + /// ``` + /// + /// [`as_mut_ptr`]: Vec::as_mut_ptr + /// [`as_ptr`]: Vec::as_ptr #[stable(feature = "vec_as_ptr", since = "1.37.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] #[inline] pub fn as_mut_ptr(&mut self) -> *mut T { // We shadow the slice method of the same name to avoid going through @@ -3403,6 +3456,36 @@ impl From<&mut [T]> for Vec { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "vec_from_array_ref", since = "1.74.0")] +impl From<&[T; N]> for Vec { + /// Allocate a `Vec` and fill it by cloning `s`'s items. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(Vec::from(&[1, 2, 3]), vec![1, 2, 3]); + /// ``` + fn from(s: &[T; N]) -> Vec { + Self::from(s.as_slice()) + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "vec_from_array_ref", since = "1.74.0")] +impl From<&mut [T; N]> for Vec { + /// Allocate a `Vec` and fill it by cloning `s`'s items. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]); + /// ``` + fn from(s: &mut [T; N]) -> Vec { + Self::from(s.as_mut_slice()) + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_from_array", since = "1.44.0")] impl From<[T; N]> for Vec { diff --git a/scripts/min-tool-version.sh b/scripts/min-tool-version.sh index fd5ffdb81bab70..c62066825f538c 100755 --- a/scripts/min-tool-version.sh +++ b/scripts/min-tool-version.sh @@ -31,7 +31,7 @@ llvm) fi ;; rustc) - echo 1.73.0 + echo 1.74.1 ;; bindgen) echo 0.65.1 From 153f5cc9b3bfd25245126d24858ef1a3e3f53035 Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Wed, 6 Dec 2023 11:59:46 +0000 Subject: [PATCH 05/18] rust: file: add Rust abstraction for `struct file` 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 Link: https://lore.kernel.org/r/20231206-alice-file-v2-1-af617c0d9d94@google.com --- 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(+) create mode 100644 rust/kernel/file.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index b5714fb69fe33d..ed06970d789a9d 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 70e59efd92bc43..03141a3608a45a 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 00000000000000..29e1aacacd0662 --- /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 e6aff80b521fd5..ce9abceab7843d 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -34,6 +34,7 @@ extern crate self as kernel; mod allocator; mod build_assert; pub mod error; +pub mod file; pub mod init; pub mod ioctl; #[cfg(CONFIG_KUNIT)] From 83b7658e77d5dadfcfa672696fa6688b6483a67b Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Wed, 6 Dec 2023 11:59:47 +0000 Subject: [PATCH 06/18] rust: cred: add Rust abstraction for `struct cred` 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 Link: https://lore.kernel.org/r/20231206-alice-file-v2-2-af617c0d9d94@google.com --- 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(+) create mode 100644 rust/kernel/cred.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ed06970d789a9d..fb7d4b0b0554fe 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 03141a3608a45a..10ed69f76424da 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 00000000000000..497058ec89bbf8 --- /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 29e1aacacd0662..a88140794a8deb 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 @@ impl 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 ce9abceab7843d..097fe9bb93ed83 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -33,6 +33,7 @@ extern crate self as kernel; #[cfg(not(testlib))] mod allocator; mod build_assert; +pub mod cred; pub mod error; pub mod file; pub mod init; From 492d054a94707a34b5e6015897e35a064921c79a Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 6 Dec 2023 11:59:48 +0000 Subject: [PATCH 07/18] rust: security: add abstraction for secctx 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 Link: https://lore.kernel.org/r/20231206-alice-file-v2-3-af617c0d9d94@google.com --- 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(+) create mode 100644 rust/kernel/security.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index fb7d4b0b0554fe..0e2a9b46459aa3 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 10ed69f76424da..fd633d9db79a38 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 497058ec89bbf8..3794937b52947d 100644 --- a/rust/kernel/cred.rs +++ b/rust/kernel/cred.rs @@ -43,6 +43,14 @@ impl 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 097fe9bb93ed83..342cb02c495aec 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -42,6 +42,7 @@ pub mod ioctl; 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 00000000000000..6545bfa2fd72e9 --- /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 71d8d22918e7577df5ec0a7a7a91340b95a391a5 Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Wed, 6 Dec 2023 11:59:49 +0000 Subject: [PATCH 08/18] rust: file: add `FileDescriptorReservation` 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 Link: https://lore.kernel.org/r/20231206-alice-file-v2-4-af617c0d9d94@google.com --- 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 a88140794a8deb..2d036d4636a0fc 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/file.rs @@ -9,9 +9,9 @@ use crate::{ 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 impl AlwaysRefCounted for File { } } +/// 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 d849e1979ac707..c0940af1239b1b 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -432,3 +432,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 0a686500964e4d1a83a9010afe3333f2fad4b598 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 6 Dec 2023 11:59:50 +0000 Subject: [PATCH 09/18] rust: file: add `Kuid` wrapper 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 Link: https://lore.kernel.org/r/20231206-alice-file-v2-5-af617c0d9d94@google.com --- 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 0e2a9b46459aa3..0499bbe3cdc58d 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 fd633d9db79a38..58e3a9dff3499d 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 3794937b52947d..fbc749788bfa3c 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 @@ impl Credential { } /// 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 b2299bc7ac1ff5..7a3a07660af7ad 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 @@ impl Task { 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 @@ impl Task { } } +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 7069c6e396004b6c8d0d67a8328b02850cbd8b7c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 6 Dec 2023 11:59:51 +0000 Subject: [PATCH 10/18] rust: file: add `DeferredFdCloser` 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 Link: https://lore.kernel.org/r/20231206-alice-file-v2-6-af617c0d9d94@google.com --- 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 0499bbe3cdc58d..6b5616499b6dad 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 58e3a9dff3499d..d146bbf25aec4a 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 2d036d4636a0fc..eba96af4bdf76c 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/file.rs @@ -11,7 +11,8 @@ use crate::{ 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 @@ impl Drop for FileDescriptorReservation { } } +/// 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 5cd4b9370220e4a4a6b80a2ce86853680c904258 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 6 Dec 2023 11:59:52 +0000 Subject: [PATCH 11/18] rust: file: add abstraction for `poll_table` 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 Link: https://lore.kernel.org/r/20231206-alice-file-v2-7-af617c0d9d94@google.com [ boqun: Removes unused POLLFREE definition ] --- rust/bindings/bindings_helper.h | 1 + rust/kernel/sync.rs | 1 + rust/kernel/sync/poll.rs | 103 ++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 rust/kernel/sync/poll.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 6b5616499b6dad..56c1471fc03cd8 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index d219ee518eff15..84726f80c4069c 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -11,6 +11,7 @@ mod arc; 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 00000000000000..e1dded9b7b9d69 --- /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() }; + } +} From 5f315752704e4eeb8cc638b0014bcbf7e2b2557f Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 23 Oct 2023 19:44:45 +0200 Subject: [PATCH 12/18] x86/rust: support RETPOLINE Support the `RETPOLINE` speculation mitigation by enabling the target features that Clang does. The existing target feature being enabled was a leftover from our old `rust` branch, and it is not enough: the target feature `retpoline-external-thunk` only implies `retpoline-indirect-calls`, but not `retpoline-indirect-branches` (see LLVM's `X86.td`), unlike Clang's flag of the same name `-mretpoline-external-thunk` which does imply both (see Clang's `lib/Driver/ToolChains/Arch/X86.cpp`). Without this, `objtool` would complain if enabled for individual object files (like it is planned in the future), e.g. rust/core.o: warning: objtool: _R...escape_default+0x13: indirect jump found in RETPOLINE build In addition, change the comment to note that LLVM is the one disabling jump tables when retpoline is enabled, thus we do not need to use `-Zno-jump-tables` for Rust here -- see commit c58f2166ab39 ("Introduce the "retpoline" x86 mitigation technique ...") [1]: The goal is simple: avoid generating code which contains an indirect branch that could have its prediction poisoned by an attacker. In many cases, the compiler can simply use directed conditional branches and a small search tree. LLVM already has support for lowering switches in this way and the first step of this patch is to disable jump-table lowering of switches and introduce a pass to rewrite explicit indirectbr sequences into a switch over integers. As well as a live example at [2]. Cc: Daniel Borkmann Link: https://github.com/llvm/llvm-project/commit/c58f2166ab3987f37cb0d7815b561bff5a20a69a [1] Link: https://godbolt.org/z/esT8xnaxj [2] Signed-off-by: Miguel Ojeda --- arch/x86/Makefile | 2 +- scripts/generate_rust_target.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 86d347ff6b3362..ca970b3670a666 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -197,7 +197,7 @@ ifdef CONFIG_RETPOLINE KBUILD_CFLAGS += $(RETPOLINE_CFLAGS) # Additionally, avoid generating expensive indirect jumps which # are subject to retpolines for small number of switch cases. - # clang turns off jump table generation by default when under + # LLVM turns off jump table generation by default when under # retpoline builds, however, gcc does not for x86. This has # only been fixed starting from gcc stable version 8.4.0 and # onwards, but not for older ones. See gcc bug #86952. diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs index ec5ef35dbe52db..fbddaf53f670c2 100644 --- a/scripts/generate_rust_target.rs +++ b/scripts/generate_rust_target.rs @@ -158,7 +158,14 @@ fn main() { ); let mut features = "-3dnow,-3dnowa,-mmx,+soft-float".to_string(); if cfg.has("RETPOLINE") { + // The kernel uses `-mretpoline-external-thunk` (for Clang), which Clang maps to the + // target feature of the same name plus the other two target features in + // `clang/lib/Driver/ToolChains/Arch/X86.cpp`. These should be eventually enabled via + // `-Ctarget-feature` when `rustc` starts recognizing them (or via a new dedicated + // flag); see https://github.com/rust-lang/rust/issues/116852. features += ",+retpoline-external-thunk"; + features += ",+retpoline-indirect-branches"; + features += ",+retpoline-indirect-calls"; } ts.push("features", features); ts.push("llvm-target", "x86_64-linux-gnu"); From 774b03483e3ab0d4dbc8e893f5942e5a698939de Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 23 Oct 2023 19:44:46 +0200 Subject: [PATCH 13/18] x86/rust: support SLS Support the `SLS` speculation mitigation by enabling the target features that Clang does. Without this, `objtool` would complain if enabled for individual object files (like it is planned in the future), e.g. rust/core.o: warning: objtool: _R...next_up+0x44: missing int3 after ret Signed-off-by: Miguel Ojeda --- scripts/generate_rust_target.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs index fbddaf53f670c2..42a6fec4f37fdc 100644 --- a/scripts/generate_rust_target.rs +++ b/scripts/generate_rust_target.rs @@ -167,6 +167,14 @@ fn main() { features += ",+retpoline-indirect-branches"; features += ",+retpoline-indirect-calls"; } + if cfg.has("SLS") { + // The kernel uses `-mharden-sls=all`, which Clang maps to both these target features in + // `clang/lib/Driver/ToolChains/Arch/X86.cpp`. These should be eventually enabled via + // `-Ctarget-feature` when `rustc` starts recognizing them (or via a new dedicated + // flag); see https://github.com/rust-lang/rust/issues/116851. + features += ",+harden-sls-ijmp"; + features += ",+harden-sls-ret"; + } ts.push("features", features); ts.push("llvm-target", "x86_64-linux-gnu"); ts.push("target-pointer-width", "64"); From c32a2ca4abf1ca1fba26f9bceda5db3f609414c9 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 23 Oct 2023 19:44:47 +0200 Subject: [PATCH 14/18] x86/rust: depend on !RETHUNK The Rust compiler does not support the equivalent of `-mfunction-return=thunk-extern` yet [1]. Thus, currently, `objtool` warns about it, e.g.: samples/rust/rust_print.o: warning: objtool: _R...init+0xa5c: 'naked' return found in RETHUNK build The support in `rustc` for `-Zfunction-return` has been submitted and is being reviewed [2]. It adds the needed LLVM function attributes and, with it, I got a RETHUNK kernel build with Rust enabled that does not print the `objtool` related warnings, boots in QEMU and can load a kernel loadable module. In any case, until proper/complete support is added to `rustc`, make it a hard restriction until the mitigation is in place. This may have an impact for developers that may not need/care about the mitigation in the Rust side (e.g. Ubuntu offers Rust as a "technology preview" [3]), but given we are getting closer to having the first actual in-tree Rust kernel users, it seems like the right time to disallow it. This should also avoid confusion [4]. Link: https://github.com/rust-lang/rust/issues/116853 [1] Link: https://github.com/rust-lang/rust/pull/116892 [2] Link: https://lore.kernel.org/rust-for-linux/ZSQXqX2%2Flhf5ICZP@gpd/ [3] Link: https://lore.kernel.org/rust-for-linux/CANiq72n6DMeXQrgOzS_+3VdgNYAmpcnneAHJnZERUQhMExg+0A@mail.gmail.com/ [4] Signed-off-by: Miguel Ojeda Acked-by: Martin Rodriguez Reboredo --- init/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/init/Kconfig b/init/Kconfig index 9ffb103fc927bc..3d4411634c7c71 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1889,6 +1889,7 @@ config RUST depends on !GCC_PLUGINS depends on !RANDSTRUCT depends on !DEBUG_INFO_BTF || PAHOLE_HAS_LANG_EXCLUDE + depends on !RETHUNK select CONSTRUCTORS help Enables Rust support in the kernel. From 033f05f05f5268931fdfa9c7fa47b80e37b58706 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 23 Oct 2023 19:44:48 +0200 Subject: [PATCH 15/18] x86/rust: support RETHUNK When support for `-Zfunction-return` lands in Rust [1], this patch may be used to enable RETHUNK support on top of the previous patch. Link: https://github.com/rust-lang/rust/pull/116892 [1] Signed-off-by: Miguel Ojeda --- arch/x86/Makefile | 5 +++++ init/Kconfig | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86/Makefile b/arch/x86/Makefile index ca970b3670a666..23e4b2e82603b7 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -24,11 +24,15 @@ RETPOLINE_CFLAGS += $(call cc-option,-mindirect-branch-cs-prefix) ifdef CONFIG_RETHUNK RETHUNK_CFLAGS := -mfunction-return=thunk-extern +RETHUNK_RUSTFLAGS := -Zfunction-return=thunk-extern RETPOLINE_CFLAGS += $(RETHUNK_CFLAGS) +RETPOLINE_RUSTFLAGS += $(RETHUNK_RUSTFLAGS) endif export RETHUNK_CFLAGS +export RETHUNK_RUSTFLAGS export RETPOLINE_CFLAGS +export RETPOLINE_RUSTFLAGS export RETPOLINE_VDSO_CFLAGS # For gcc stack alignment is specified with -mpreferred-stack-boundary, @@ -195,6 +199,7 @@ KBUILD_CFLAGS += -fno-asynchronous-unwind-tables # Avoid indirect branches in kernel to deal with Spectre ifdef CONFIG_RETPOLINE KBUILD_CFLAGS += $(RETPOLINE_CFLAGS) + KBUILD_RUSTFLAGS += $(RETPOLINE_RUSTFLAGS) # Additionally, avoid generating expensive indirect jumps which # are subject to retpolines for small number of switch cases. # LLVM turns off jump table generation by default when under diff --git a/init/Kconfig b/init/Kconfig index 3d4411634c7c71..9ffb103fc927bc 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1889,7 +1889,6 @@ config RUST depends on !GCC_PLUGINS depends on !RANDSTRUCT depends on !DEBUG_INFO_BTF || PAHOLE_HAS_LANG_EXCLUDE - depends on !RETHUNK select CONSTRUCTORS help Enables Rust support in the kernel. From 54a3c8ec56a4bb53d461e65b9866314a93f5be60 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Wed, 6 Dec 2023 15:11:01 -0800 Subject: [PATCH 16/18] rust: port file::Operations and deps from rust branch Port file::Operations and its dependencies from the `rust` branch. This is a trimmed version from what can be found in `rust` since I only need a subset of the functionality. Signed-off-by: Fabien Parent --- rust/helpers.c | 18 +++ rust/kernel/file.rs | 295 ++++++++++++++++++++++++++++++++++++++- rust/kernel/io_buffer.rs | 163 +++++++++++++++++++++ rust/kernel/iov_iter.rs | 81 +++++++++++ rust/kernel/lib.rs | 4 + rust/kernel/types.rs | 19 +++ rust/kernel/user_ptr.rs | 183 ++++++++++++++++++++++++ 7 files changed, 761 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/io_buffer.rs create mode 100644 rust/kernel/iov_iter.rs create mode 100644 rust/kernel/user_ptr.rs diff --git a/rust/helpers.c b/rust/helpers.c index d146bbf25aec4a..838ddf84554bc2 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -251,6 +251,24 @@ void rust_helper_init_task_work(struct callback_head *twork, } EXPORT_SYMBOL_GPL(rust_helper_init_task_work); +unsigned long rust_helper_copy_from_user(void *to, const void __user *from, unsigned long n) +{ + return copy_from_user(to, from, n); +} +EXPORT_SYMBOL_GPL(rust_helper_copy_from_user); + +unsigned long rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n) +{ + return copy_to_user(to, from, n); +} +EXPORT_SYMBOL_GPL(rust_helper_copy_to_user); + +unsigned long rust_helper_clear_user(void __user *to, unsigned long n) +{ + return clear_user(to, n); +} +EXPORT_SYMBOL_GPL(rust_helper_clear_user); + /* * `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 eba96af4bdf76c..8f3db7714ec5e2 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/file.rs @@ -8,8 +8,11 @@ use crate::{ bindings, cred::Credential, - error::{code::*, Error, Result}, - types::{ARef, AlwaysRefCounted, NotThreadSafe, Opaque}, + error::{code::*, from_result, Error, Result}, + io_buffer::{IoBufferReader, IoBufferWriter}, + macros::vtable, + types::{ARef, AlwaysRefCounted, ForeignOwnable, NotThreadSafe, Opaque}, + user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter}, }; use alloc::boxed::Box; use core::{alloc::AllocError, marker::PhantomData, mem, ptr}; @@ -429,3 +432,291 @@ impl core::fmt::Debug for BadFdError { f.pad("EBADF") } } + +/// Equivalent to [`std::io::SeekFrom`]. +/// +/// [`std::io::SeekFrom`]: https://doc.rust-lang.org/std/io/enum.SeekFrom.html +pub enum SeekFrom { + /// Equivalent to C's `SEEK_SET`. + Start(u64), + + /// Equivalent to C's `SEEK_END`. + End(i64), + + /// Equivalent to C's `SEEK_CUR`. + Current(i64), +} + +pub(crate) struct OperationsVtable(PhantomData, PhantomData); + +impl, T: Operations> OperationsVtable { + /// Called by the VFS when an inode should be opened. + /// + /// Calls `T::open` on the returned value of `A::convert`. + /// + /// # Safety + /// + /// The returned value of `A::convert` must be a valid non-null pointer and + /// `T:open` must return a valid non-null pointer on an `Ok` result. + unsafe extern "C" fn open_callback( + inode: *mut bindings::inode, + file: *mut bindings::file, + ) -> core::ffi::c_int { + from_result(|| { + // SAFETY: `A::convert` must return a valid non-null pointer that + // should point to data in the inode or file that lives longer + // than the following use of `T::open`. + let arg = unsafe { A::convert(inode, file) }; + // SAFETY: The C contract guarantees that `file` is valid. Additionally, + // `fileref` never outlives this function, so it is guaranteed to be + // valid. + let fileref = unsafe { File::from_ptr(file) }; + // SAFETY: `arg` was previously returned by `A::convert` and must + // be a valid non-null pointer. + let ptr = T::open(unsafe { &*arg }, fileref)?.into_foreign(); + // SAFETY: The C contract guarantees that `private_data` is available + // for implementers of the file operations (no other C code accesses + // it), so we know that there are no concurrent threads/CPUs accessing + // it (it's not visible to any other Rust code). + unsafe { (*file).private_data = ptr as *mut core::ffi::c_void }; + Ok(0) + }) + } + + unsafe extern "C" fn read_callback( + file: *mut bindings::file, + buf: *mut core::ffi::c_char, + len: core::ffi::c_size_t, + offset: *mut bindings::loff_t, + ) -> core::ffi::c_ssize_t { + from_result(|| { + let mut data = + unsafe { UserSlicePtr::new(buf as *mut core::ffi::c_void, len).writer() }; + // SAFETY: `private_data` was initialised by `open_callback` with a value returned by + // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the + // `release` callback, which the C API guarantees that will be called only when all + // references to `file` have been released, so we know it can't be called while this + // function is running. + let f = unsafe { T::Data::borrow((*file).private_data) }; + // No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63). + // See . + let read = T::read( + f, + unsafe { File::from_ptr(file) }, + &mut data, + unsafe { *offset }.try_into()?, + )?; + unsafe { (*offset) += bindings::loff_t::try_from(read).unwrap() }; + Ok(read as _) + }) + } + + unsafe extern "C" fn write_callback( + file: *mut bindings::file, + buf: *const core::ffi::c_char, + len: core::ffi::c_size_t, + offset: *mut bindings::loff_t, + ) -> core::ffi::c_ssize_t { + from_result(|| { + let mut data = + unsafe { UserSlicePtr::new(buf as *mut core::ffi::c_void, len).reader() }; + // SAFETY: `private_data` was initialised by `open_callback` with a value returned by + // `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the + // `release` callback, which the C API guarantees that will be called only when all + // references to `file` have been released, so we know it can't be called while this + // function is running. + let f = unsafe { T::Data::borrow((*file).private_data) }; + // No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63). + // See . + let written = T::write( + f, + unsafe { File::from_ptr(file) }, + &mut data, + unsafe { *offset }.try_into()?, + )?; + unsafe { (*offset) += bindings::loff_t::try_from(written).unwrap() }; + Ok(written as _) + }) + } + + unsafe extern "C" fn release_callback( + _inode: *mut bindings::inode, + file: *mut bindings::file, + ) -> core::ffi::c_int { + let ptr = mem::replace(unsafe { &mut (*file).private_data }, ptr::null_mut()); + T::release(unsafe { T::Data::from_foreign(ptr as _) }, unsafe { + File::from_ptr(file) + }); + 0 + } + + const VTABLE: bindings::file_operations = bindings::file_operations { + open: Some(Self::open_callback), + release: Some(Self::release_callback), + read: if T::HAS_READ { + Some(Self::read_callback) + } else { + None + }, + write: if T::HAS_WRITE { + Some(Self::write_callback) + } else { + None + }, + llseek: None, + check_flags: None, + compat_ioctl: None, + copy_file_range: None, + fallocate: None, + fadvise: None, + fasync: None, + flock: None, + flush: None, + fsync: None, + get_unmapped_area: None, + iterate_shared: None, + iopoll: None, + lock: None, + mmap: None, + mmap_supported_flags: 0, + owner: ptr::null_mut(), + poll: None, + read_iter: None, + remap_file_range: None, + setlease: None, + show_fdinfo: None, + splice_eof: None, + splice_read: None, + splice_write: None, + unlocked_ioctl: None, + uring_cmd: None, + uring_cmd_iopoll: None, + write_iter: None, + }; + + /// Builds an instance of [`struct file_operations`]. + /// + /// # Safety + /// + /// The caller must ensure that the adapter is compatible with the way the device is registered. + pub(crate) const unsafe fn build() -> &'static bindings::file_operations { + &Self::VTABLE + } +} + +/// Allows the handling of ioctls defined with the `_IO`, `_IOR`, `_IOW`, and `_IOWR` macros. +/// +/// For each macro, there is a handler function that takes the appropriate types as arguments. +pub trait IoctlHandler: Sync { + /// The type of the first argument to each associated function. + type Target<'a>; + + /// Handles ioctls defined with the `_IO` macro, that is, with no buffer as argument. + fn pure(_this: Self::Target<'_>, _file: &File, _cmd: u32, _arg: usize) -> Result { + Err(EINVAL) + } + + /// Handles ioctls defined with the `_IOR` macro, that is, with an output buffer provided as + /// argument. + fn read( + _this: Self::Target<'_>, + _file: &File, + _cmd: u32, + _writer: &mut UserSlicePtrWriter, + ) -> Result { + Err(EINVAL) + } + + /// Handles ioctls defined with the `_IOW` macro, that is, with an input buffer provided as + /// argument. + fn write( + _this: Self::Target<'_>, + _file: &File, + _cmd: u32, + _reader: &mut UserSlicePtrReader, + ) -> Result { + Err(EINVAL) + } + + /// Handles ioctls defined with the `_IOWR` macro, that is, with a buffer for both input and + /// output provided as argument. + fn read_write( + _this: Self::Target<'_>, + _file: &File, + _cmd: u32, + _data: UserSlicePtr, + ) -> Result { + Err(EINVAL) + } +} + +/// Trait for extracting file open arguments from kernel data structures. +/// +/// This is meant to be implemented by registration managers. +pub trait OpenAdapter { + /// Converts untyped data stored in [`struct inode`] and [`struct file`] (when [`struct + /// file_operations::open`] is called) into the given type. For example, for `miscdev` + /// devices, a pointer to the registered [`struct miscdev`] is stored in [`struct + /// file::private_data`]. + /// + /// # Safety + /// + /// This function must be called only when [`struct file_operations::open`] is being called for + /// a file that was registered by the implementer. The returned pointer must be valid and + /// not-null. + unsafe fn convert(_inode: *mut bindings::inode, _file: *mut bindings::file) -> *const T; +} + +/// Corresponds to the kernel's `struct file_operations`. +/// +/// You implement this trait whenever you would create a `struct file_operations`. +/// +/// File descriptors may be used from multiple threads/processes concurrently, so your type must be +/// [`Sync`]. It must also be [`Send`] because [`Operations::release`] will be called from the +/// thread that decrements that associated file's refcount to zero. +#[vtable] +pub trait Operations { + /// The type of the context data returned by [`Operations::open`] and made available to + /// other methods. + type Data: ForeignOwnable + Send + Sync = (); + + /// The type of the context data passed to [`Operations::open`]. + type OpenData: Sync = (); + + /// Creates a new instance of this file. + /// + /// Corresponds to the `open` function pointer in `struct file_operations`. + fn open(context: &Self::OpenData, file: &File) -> Result; + + /// Cleans up after the last reference to the file goes away. + /// + /// Note that context data is moved, so it will be freed automatically unless the + /// implementation moves it elsewhere. + /// + /// Corresponds to the `release` function pointer in `struct file_operations`. + fn release(_data: Self::Data, _file: &File) {} + + /// Reads data from this file to the caller's buffer. + /// + /// Corresponds to the `read` and `read_iter` function pointers in `struct file_operations`. + fn read( + _data: ::Borrowed<'_>, + _file: &File, + _writer: &mut impl IoBufferWriter, + _offset: u64, + ) -> Result { + Err(EINVAL) + } + + /// Writes data from the caller's buffer to this file. + /// + /// Corresponds to the `write` and `write_iter` function pointers in `struct file_operations`. + fn write( + _data: ::Borrowed<'_>, + _file: &File, + _reader: &mut impl IoBufferReader, + _offset: u64, + ) -> Result { + Err(EINVAL) + } +} diff --git a/rust/kernel/io_buffer.rs b/rust/kernel/io_buffer.rs new file mode 100644 index 00000000000000..17ebd6d248055b --- /dev/null +++ b/rust/kernel/io_buffer.rs @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Buffers used in IO. + +use crate::error::Result; +use alloc::vec::Vec; +use core::mem::{size_of, MaybeUninit}; + +/// Represents a buffer to be read from during IO. +pub trait IoBufferReader { + /// Returns the number of bytes left to be read from the io buffer. + /// + /// Note that even reading less than this number of bytes may fail. + fn len(&self) -> usize; + + /// Returns the underlying buffer if available. + fn buffer(&self) -> Option<*mut core::ffi::c_void> { + None + } + + /// Returns trus if there is nothing left to be read from the buffer. + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Reads raw data from the io buffer into a raw kernel buffer. + /// + /// # Safety + /// + /// The output buffer must be valid. + unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> Result; + + /// Reads all data remaining in the io buffer. + /// + /// Returns `EFAULT` if the address does not currently point to mapped, readable memory. + fn read_all(&mut self) -> Result> { + let mut data = Vec::::new(); + data.try_resize(self.len(), 0)?; + + // SAFETY: The output buffer is valid as we just allocated it. + unsafe { self.read_raw(data.as_mut_ptr(), data.len())? }; + Ok(data) + } + + /// Reads a byte slice from the io buffer. + /// + /// Returns `EFAULT` if the byte slice is bigger than the remaining size of the user slice or + /// if the address does not currently point to mapped, readable memory. + fn read_slice(&mut self, data: &mut [u8]) -> Result { + // SAFETY: The output buffer is valid as it's coming from a live reference. + unsafe { self.read_raw(data.as_mut_ptr(), data.len()) } + } + + /// Reads the contents of a plain old data (POD) type from the io buffer. + fn read(&mut self) -> Result { + let mut out = MaybeUninit::::uninit(); + // SAFETY: The buffer is valid as it was just allocated. + unsafe { self.read_raw(out.as_mut_ptr() as _, size_of::()) }?; + // SAFETY: We just initialised the data. + Ok(unsafe { out.assume_init() }) + } +} + +/// Represents a buffer to be written to during IO. +pub trait IoBufferWriter { + /// Returns the number of bytes left to be written into the io buffer. + /// + /// Note that even writing less than this number of bytes may fail. + fn len(&self) -> usize; + + /// Returns `true` if the io buffer cannot hold any additional data. + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the backing io buffer + fn buffer(&self) -> Option<*mut core::ffi::c_void> { + None + } + + /// Writes zeroes to the io buffer. + /// + /// Differently from the other write functions, `clear` will zero as much as it can and update + /// the writer internal state to reflect this. It will, however, return an error if it cannot + /// clear `len` bytes. + /// + /// For example, if a caller requests that 100 bytes be cleared but a segfault happens after + /// 20 bytes, then EFAULT is returned and the writer is advanced by 20 bytes. + fn clear(&mut self, len: usize) -> Result; + + /// Writes a byte slice into the io buffer. + /// + /// Returns `EFAULT` if the byte slice is bigger than the remaining size of the io buffer or if + /// the address does not currently point to mapped, writable memory. + fn write_slice(&mut self, data: &[u8]) -> Result { + // SAFETY: The input buffer is valid as it's coming from a live reference. + unsafe { self.write_raw(data.as_ptr(), data.len()) } + } + + /// Writes raw data to the io buffer from a raw kernel buffer. + /// + /// # Safety + /// + /// The input buffer must be valid. + unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> Result; + + /// Writes the contents of the given data into the io buffer. + fn write(&mut self, data: &T) -> Result { + // SAFETY: The input buffer is valid as it's coming from a live + // reference to a type that implements `WritableToBytes`. + unsafe { self.write_raw(data as *const T as _, size_of::()) } + } +} + +/// Specifies that a type is safely readable from byte slices. +/// +/// Not all types can be safely read from byte slices; examples from +/// include `bool` +/// that must be either `0` or `1`, and `char` that cannot be a surrogate or above `char::MAX`. +/// +/// # Safety +/// +/// Implementers must ensure that the type is made up only of types that can be safely read from +/// arbitrary byte sequences (e.g., `u32`, `u64`, etc.). +pub unsafe trait ReadableFromBytes {} + +// SAFETY: All bit patterns are acceptable values of the types below. +unsafe impl ReadableFromBytes for u8 {} +unsafe impl ReadableFromBytes for u16 {} +unsafe impl ReadableFromBytes for u32 {} +unsafe impl ReadableFromBytes for u64 {} +unsafe impl ReadableFromBytes for usize {} +unsafe impl ReadableFromBytes for i8 {} +unsafe impl ReadableFromBytes for i16 {} +unsafe impl ReadableFromBytes for i32 {} +unsafe impl ReadableFromBytes for i64 {} +unsafe impl ReadableFromBytes for isize {} + +/// Specifies that a type is safely writable to byte slices. +/// +/// This means that we don't read undefined values (which leads to UB) in preparation for writing +/// to the byte slice. It also ensures that no potentially sensitive information is leaked into the +/// byte slices. +/// +/// # Safety +/// +/// A type must not include padding bytes and must be fully initialised to safely implement +/// [`WritableToBytes`] (i.e., it doesn't contain [`MaybeUninit`] fields). A composition of +/// writable types in a structure is not necessarily writable because it may result in padding +/// bytes. +pub unsafe trait WritableToBytes {} + +// SAFETY: Initialised instances of the following types have no uninitialised portions. +unsafe impl WritableToBytes for u8 {} +unsafe impl WritableToBytes for u16 {} +unsafe impl WritableToBytes for u32 {} +unsafe impl WritableToBytes for u64 {} +unsafe impl WritableToBytes for usize {} +unsafe impl WritableToBytes for i8 {} +unsafe impl WritableToBytes for i16 {} +unsafe impl WritableToBytes for i32 {} +unsafe impl WritableToBytes for i64 {} +unsafe impl WritableToBytes for isize {} diff --git a/rust/kernel/iov_iter.rs b/rust/kernel/iov_iter.rs new file mode 100644 index 00000000000000..4ea0b93f77a386 --- /dev/null +++ b/rust/kernel/iov_iter.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! IO vector iterators. +//! +//! C header: [`include/linux/uio.h`](../../../../include/linux/uio.h) + +use crate::{ + bindings, + error::code::*, + error::Result, + io_buffer::{IoBufferReader, IoBufferWriter}, +}; + +/// Wraps the kernel's `struct iov_iter`. +/// +/// # Invariants +/// +/// The pointer `IovIter::ptr` is non-null and valid. +pub struct IovIter { + ptr: *mut bindings::iov_iter, +} + +impl IovIter { + fn common_len(&self) -> usize { + // SAFETY: `IovIter::ptr` is guaranteed to be valid by the type invariants. + unsafe { (*self.ptr).count } + } + + /// Constructs a new [`struct iov_iter`] wrapper. + /// + /// # Safety + /// + /// The pointer `ptr` must be non-null and valid for the lifetime of the object. + pub(crate) unsafe fn from_ptr(ptr: *mut bindings::iov_iter) -> Self { + // INVARIANTS: the safety contract ensures the type invariant will hold. + Self { ptr } + } +} + +impl IoBufferWriter for IovIter { + fn len(&self) -> usize { + self.common_len() + } + + fn clear(&mut self, mut len: usize) -> Result { + while len > 0 { + // SAFETY: `IovIter::ptr` is guaranteed to be valid by the type invariants. + let written = unsafe { bindings::iov_iter_zero(len, self.ptr) }; + if written == 0 { + return Err(EFAULT); + } + + len -= written; + } + Ok(()) + } + + unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> Result { + let res = unsafe { bindings::copy_to_iter(data as _, len, self.ptr) }; + if res != len { + Err(EFAULT) + } else { + Ok(()) + } + } +} + +impl IoBufferReader for IovIter { + fn len(&self) -> usize { + self.common_len() + } + + unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> Result { + let res = unsafe { bindings::copy_from_iter(out as _, len, self.ptr) }; + if res != len { + Err(EFAULT) + } else { + Ok(()) + } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 342cb02c495aec..8a4e76ebbfbede 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -13,7 +13,9 @@ #![no_std] #![feature(allocator_api)] +#![feature(associated_type_defaults)] #![feature(coerce_unsized)] +#![feature(c_size_t)] #![feature(dispatch_from_dyn)] #![feature(new_uninit)] #![feature(offset_of)] @@ -38,6 +40,7 @@ pub mod error; pub mod file; pub mod init; pub mod ioctl; +pub mod io_buffer; #[cfg(CONFIG_KUNIT)] pub mod kunit; pub mod prelude; @@ -50,6 +53,7 @@ pub mod str; pub mod sync; pub mod task; pub mod types; +pub mod user_ptr; pub mod workqueue; #[doc(hidden)] diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index c0940af1239b1b..01ddf5f3a9e142 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -12,6 +12,25 @@ use core::{ ptr::NonNull, }; +/// Permissions. +/// +/// C header: [`include/uapi/linux/stat.h`](../../../../include/uapi/linux/stat.h) +/// +/// C header: [`include/linux/stat.h`](../../../../include/linux/stat.h) +pub struct Mode(bindings::umode_t); + +impl Mode { + /// Creates a [`Mode`] from an integer. + pub fn from_int(m: u16) -> Mode { + Mode(m) + } + + /// Returns the mode as an integer. + pub fn as_int(&self) -> u16 { + self.0 + } +} + /// Used to transfer ownership to and from foreign (non-Rust) languages. /// /// Ownership is transferred from Rust to a foreign language by calling [`Self::into_foreign`] and diff --git a/rust/kernel/user_ptr.rs b/rust/kernel/user_ptr.rs new file mode 100644 index 00000000000000..cf64e27c709bf8 --- /dev/null +++ b/rust/kernel/user_ptr.rs @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! User pointers. +//! +//! C header: [`include/linux/uaccess.h`](../../../../include/linux/uaccess.h) + +use crate::{ + bindings, + error::code::*, + error::Result, + io_buffer::{IoBufferReader, IoBufferWriter}, +}; +use alloc::vec::Vec; + +/// A reference to an area in userspace memory, which can be either +/// read-only or read-write. +/// +/// All methods on this struct are safe: invalid pointers return +/// `EFAULT`. Concurrent access, *including data races to/from userspace +/// memory*, is permitted, because fundamentally another userspace +/// thread/process could always be modifying memory at the same time +/// (in the same way that userspace Rust's [`std::io`] permits data races +/// with the contents of files on disk). In the presence of a race, the +/// exact byte values read/written are unspecified but the operation is +/// well-defined. Kernelspace code should validate its copy of data +/// after completing a read, and not expect that multiple reads of the +/// same address will return the same value. +/// +/// All APIs enforce the invariant that a given byte of memory from userspace +/// may only be read once. By preventing double-fetches we avoid TOCTOU +/// vulnerabilities. This is accomplished by taking `self` by value to prevent +/// obtaining multiple readers on a given [`UserSlicePtr`], and the readers +/// only permitting forward reads. +/// +/// Constructing a [`UserSlicePtr`] performs no checks on the provided +/// address and length, it can safely be constructed inside a kernel thread +/// with no current userspace process. Reads and writes wrap the kernel APIs +/// `copy_from_user` and `copy_to_user`, which check the memory map of the +/// current process and enforce that the address range is within the user +/// range (no additional calls to `access_ok` are needed). +/// +/// [`std::io`]: https://doc.rust-lang.org/std/io/index.html +pub struct UserSlicePtr(*mut core::ffi::c_void, usize); + +impl UserSlicePtr { + /// Constructs a user slice from a raw pointer and a length in bytes. + /// + /// # Safety + /// + /// Callers must be careful to avoid time-of-check-time-of-use + /// (TOCTOU) issues. The simplest way is to create a single instance of + /// [`UserSlicePtr`] per user memory block as it reads each byte at + /// most once. + pub unsafe fn new(ptr: *mut core::ffi::c_void, length: usize) -> Self { + UserSlicePtr(ptr, length) + } + + /// Reads the entirety of the user slice. + /// + /// Returns `EFAULT` if the address does not currently point to + /// mapped, readable memory. + pub fn read_all(self) -> Result> { + self.reader().read_all() + } + + /// Constructs a [`UserSlicePtrReader`]. + pub fn reader(self) -> UserSlicePtrReader { + UserSlicePtrReader(self.0, self.1) + } + + /// Writes the provided slice into the user slice. + /// + /// Returns `EFAULT` if the address does not currently point to + /// mapped, writable memory (in which case some data from before the + /// fault may be written), or `data` is larger than the user slice + /// (in which case no data is written). + pub fn write_all(self, data: &[u8]) -> Result { + self.writer().write_slice(data) + } + + /// Constructs a [`UserSlicePtrWriter`]. + pub fn writer(self) -> UserSlicePtrWriter { + UserSlicePtrWriter(self.0, self.1) + } + + /// Constructs both a [`UserSlicePtrReader`] and a [`UserSlicePtrWriter`]. + pub fn reader_writer(self) -> (UserSlicePtrReader, UserSlicePtrWriter) { + ( + UserSlicePtrReader(self.0, self.1), + UserSlicePtrWriter(self.0, self.1), + ) + } +} + +/// A reader for [`UserSlicePtr`]. +/// +/// Used to incrementally read from the user slice. +pub struct UserSlicePtrReader(*mut core::ffi::c_void, usize); + +impl IoBufferReader for UserSlicePtrReader { + /// Returns the number of bytes left to be read from this. + /// + /// Note that even reading less than this number of bytes may fail. + fn len(&self) -> usize { + self.1 + } + + fn buffer(&self) -> Option<*mut core::ffi::c_void> { + Some(self.0) + } + + /// Reads raw data from the user slice into a raw kernel buffer. + /// + /// # Safety + /// + /// The output buffer must be valid. + unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> Result { + if len > self.1 || len > u32::MAX as usize { + return Err(EFAULT); + } + let res = unsafe { bindings::copy_from_user(out as _, self.0, len as _) }; + if res != 0 { + return Err(EFAULT); + } + // Since this is not a pointer to a valid object in our program, + // we cannot use `add`, which has C-style rules for defined + // behavior. + self.0 = self.0.wrapping_add(len); + self.1 -= len; + Ok(()) + } +} + +/// A writer for [`UserSlicePtr`]. +/// +/// Used to incrementally write into the user slice. +pub struct UserSlicePtrWriter(*mut core::ffi::c_void, usize); + +impl IoBufferWriter for UserSlicePtrWriter { + fn len(&self) -> usize { + self.1 + } + + fn buffer(&self) -> Option<*mut core::ffi::c_void> { + Some(self.0) + } + + fn clear(&mut self, mut len: usize) -> Result { + let mut ret = Ok(()); + if len > self.1 { + ret = Err(EFAULT); + len = self.1; + } + + // SAFETY: The buffer will be validated by `clear_user`. We ensure that `len` is within + // bounds in the check above. + let left = unsafe { bindings::clear_user(self.0, len as _) } as usize; + if left != 0 { + ret = Err(EFAULT); + len -= left; + } + + self.0 = self.0.wrapping_add(len); + self.1 -= len; + ret + } + + unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> Result { + if len > self.1 || len > u32::MAX as usize { + return Err(EFAULT); + } + let res = unsafe { bindings::copy_to_user(self.0, data as _, len as _) }; + if res != 0 { + return Err(EFAULT); + } + // Since this is not a pointer to a valid object in our program, + // we cannot use `add`, which has C-style rules for defined + // behavior. + self.0 = self.0.wrapping_add(len); + self.1 -= len; + Ok(()) + } +} From a6ba5e09c9e87b096cfef069ccf76f9c33fbe424 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Wed, 3 May 2023 00:59:31 +0200 Subject: [PATCH 17/18] rust: add debugfs wrapper Signed-off-by: Fabien Parent --- rust/bindings/bindings_helper.h | 1 + rust/kernel/debugfs.rs | 367 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 3 files changed, 370 insertions(+) create mode 100644 rust/kernel/debugfs.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 56c1471fc03cd8..7fe50f2a4b687d 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs new file mode 100644 index 00000000000000..80f554a273fda4 --- /dev/null +++ b/rust/kernel/debugfs.rs @@ -0,0 +1,367 @@ +#![allow(missing_docs)] +// SPDX-License-Identifier: GPL-2.0 + +//! API to add files to debugfs. +//! +//! C header: [`include/linux/debugfs.h`](../../../../include/linux/debugfs.h) +//! +//! Reference: + +use crate::error::{from_err_ptr, Result}; +use crate::file; +use crate::prelude::*; +use crate::str::CStr; +use crate::sync::Arc; +use crate::types::Mode; +use core::ffi::c_void; + +pub type PinnedRegistration = Pin>>; + +/// A registration of a debugfs directory or file +pub struct Registration { + open_data: T, + dentry: *mut bindings::dentry, + _parent: Option>>, // Store parent to prevent it from being dropped +} + +// SAFETY: dentry is only being held by the struct and is not shared with anyone else, so if T is +// Send, it is safe to send this struct to another thread. +unsafe impl Send for Registration {} + +// SAFETY: dentry is never accessed except in Registration::drop. As long as T is Sync, then +// it is safe for Registration to be Sync +unsafe impl Sync for Registration {} + +impl Drop for Registration { + fn drop(&mut self) { + // SAFETY: self.dentry is valid by the type invariant. + unsafe { + bindings::debugfs_remove(self.dentry); + } + } +} + +impl Registration<()> { + pub fn register_symlink( + name: &'static CStr, + parent: Option>>, + dest: &'static CStr, + ) -> Result> { + let parent_dentry = parent.as_ref().map_or(core::ptr::null_mut(), |r| r.dentry); + + let dentry = from_err_ptr(unsafe { + bindings::debugfs_create_symlink(name.as_char_ptr(), parent_dentry, dest.as_char_ptr()) + })?; + + Ok(Self { + dentry, + open_data: (), + _parent: parent, + }) + } + + pub fn register_dir( + name: &'static CStr, + parent: Option>>, + ) -> Result> { + let parent_dentry = parent.as_ref().map_or(core::ptr::null_mut(), |r| r.dentry); + + // SAFETY: name.as_char_ptr() cannot be null. The type invariant ensure that + // self.dentry is always a valid pointer, so p will always be a NULL pointer or a valid + // pointer. + let dentry = from_err_ptr(unsafe { + bindings::debugfs_create_dir(name.as_char_ptr(), parent_dentry) + })?; + + Ok(Self { + dentry, + open_data: (), + _parent: parent, + }) + } +} + +impl Registration { + pub fn register_file( + name: &'static CStr, + mode: Mode, + open_data: T, + parent: Option>>, + ) -> Result> + where + Self: file::OpenAdapter, + U: file::Operations, + { + let fops = unsafe { file::OperationsVtable::::build() }; + let parent_dentry = parent.as_ref().map_or(core::ptr::null_mut(), |r| r.dentry); + + let mut registration = Pin::from(Box::try_new(Self { + dentry: core::ptr::null_mut(), + open_data, + _parent: parent, + })?); + // SAFETY: The function never moves `this` hence the call is safe. + let this = unsafe { registration.as_mut().get_unchecked_mut() }; + this.dentry = from_err_ptr(unsafe { + bindings::debugfs_create_file( + name.as_char_ptr(), + mode.as_int(), + parent_dentry, + this as *mut _ as *mut c_void, + fops, + ) + })?; + + Ok(registration) + } +} + +impl file::OpenAdapter for Registration { + // WIP: Returns a valid pointer that lives longer than the call to the open function + unsafe fn convert(inode: *mut bindings::inode, _file: *mut bindings::file) -> *const T { + // SAFETY: debugfs_create_file is called with self as private data. The C debugfs API + // stores it into the inode.i_private field. + let this: &Self = unsafe { &*((*inode).i_private as *const Self) }; + &this.open_data + } +} + +pub mod attr { + use super::Registration; + use crate::error::Result; + use crate::file; + use crate::prelude::*; + use crate::str::CStr; + use crate::sync::{Arc, ArcBorrow}; + use core::ffi::{c_int, c_void}; + + pub trait Attribute { + fn get(&self) -> Result; + fn set(&self, val: T) -> Result; + } + + pub struct AttributeData { + private_data: *mut c_void, + } + + unsafe impl Sync for AttributeData {} + unsafe impl Send for AttributeData {} + + pub fn open( + file: &file::File, + get: Option c_int>, + set: Option c_int>, + fmt: &CStr, + ) -> Result> { + let file = file.as_ptr(); + + let private_data = unsafe { + bindings::simple_attr_open((*file).f_inode, file, get, set, fmt.as_char_ptr()); + (*file).private_data + }; + + Ok(Arc::try_new(AttributeData { private_data })?) + } + + pub fn release(_data: Arc, file: &file::File) { + let file = file.as_ptr(); + unsafe { bindings::simple_attr_release((*file).f_inode, file) }; + } + + pub fn read( + data: ArcBorrow<'_, AttributeData>, + file: &file::File, + writer: &mut impl crate::io_buffer::IoBufferWriter, + offset: u64, + ) -> Result { + let mut ppos = offset as bindings::loff_t; + let file = file.as_ptr(); + let buf = writer.buffer().unwrap() as *mut i8; + + let ret = unsafe { + let private_data = (*file).private_data; + (*file).private_data = data.private_data; + let ret = bindings::debugfs_attr_read(file, buf, writer.len(), &mut ppos); + (*file).private_data = private_data; + ret + }; + + if ret < 0 { + Err(Error::from_errno(ret as i32)) + } else { + Ok(ret as usize) + } + } + + pub fn write( + data: ArcBorrow<'_, AttributeData>, + file: &file::File, + reader: &mut impl crate::io_buffer::IoBufferReader, + offset: u64, + signed: bool, + ) -> Result { + let mut ppos = offset as bindings::loff_t; + let file = file.as_ptr(); + let buf = reader.buffer().unwrap() as *mut i8; + + let ret = unsafe { + let private_data = (*file).private_data; + (*file).private_data = data.private_data; + let ret = match signed { + true => bindings::debugfs_attr_write_signed(file, buf, reader.len(), &mut ppos), + false => bindings::debugfs_attr_write(file, buf, reader.len(), &mut ppos), + }; + (*file).private_data = private_data; + ret + }; + + if ret < 0 { + Err(Error::from_errno(ret as i32)) + } else { + Ok(ret as usize) + } + } + + pub extern "C" fn _get_callback_unsigned>( + this: *mut core::ffi::c_void, + val: *mut u64, + ) -> core::ffi::c_int { + let this: &Registration> = unsafe { &mut *(this as *mut _) }; + match this.open_data.get() { + Ok(v) => { + unsafe { *val = v }; + 0 + } + Err(e) => e.to_errno(), + } + } + + pub extern "C" fn _get_callback_signed>( + this: *mut core::ffi::c_void, + val: *mut u64, + ) -> core::ffi::c_int { + let this: &Registration> = unsafe { &mut *(this as *mut _) }; + match this.open_data.get() { + Ok(v) => { + unsafe { *val = v as u64 }; + 0 + } + Err(e) => e.to_errno(), + } + } + + pub extern "C" fn _set_callback_unsigned>( + this: *mut core::ffi::c_void, + val: u64, + ) -> core::ffi::c_int { + let this: &Registration> = unsafe { &mut *(this as *mut _) }; + match this.open_data.set(val) { + Ok(_) => 0, + Err(e) => e.to_errno(), + } + } + + pub extern "C" fn _set_callback_signed>( + this: *mut core::ffi::c_void, + val: u64, + ) -> core::ffi::c_int { + let this: &Registration> = unsafe { &mut *(this as *mut _) }; + match this.open_data.set(val as i64) { + Ok(_) => 0, + Err(e) => e.to_errno(), + } + } +} + +#[macro_export] +macro_rules! attribute { + ($attribute_type:ty, $fmt:literal, $is_signed:literal, $getter:expr, $setter:expr) => { + impl $attribute_type { + fn register( + self: $crate::sync::Arc, + name: &'static $crate::str::CStr, + mode: $crate::types::Mode, + parent: ::core::option::Option< + $crate::sync::Arc<$crate::debugfs::Registration<()>>, + >, + ) -> $crate::error::Result<$crate::debugfs::PinnedRegistration<$crate::sync::Arc>> + { + $crate::debugfs::Registration::<$crate::sync::Arc>::register_file::( + name, mode, self, parent, + ) + } + } + + #[vtable] + impl $crate::file::Operations for $attribute_type { + type OpenData = $crate::sync::Arc; + type Data = $crate::sync::Arc<$crate::debugfs::attr::AttributeData>; + + fn open( + data: &Self::OpenData, + file: &$crate::file::File, + ) -> $crate::error::Result { + use ::core::option::Option::Some; + + $crate::debugfs::attr::open( + file, + Some($getter), + Some($setter), + $crate::c_str!($fmt), + ) + } + + fn release(data: Self::Data, file: &$crate::file::File) { + $crate::debugfs::attr::release(data, file); + } + + fn read( + data: $crate::sync::ArcBorrow<'_, $crate::debugfs::attr::AttributeData>, + file: &$crate::file::File, + writer: &mut impl $crate::io_buffer::IoBufferWriter, + offset: u64, + ) -> $crate::error::Result { + $crate::debugfs::attr::read(data, file, writer, offset) + } + + fn write( + data: $crate::sync::ArcBorrow<'_, $crate::debugfs::attr::AttributeData>, + file: &$crate::file::File, + reader: &mut impl $crate::io_buffer::IoBufferReader, + offset: u64, + ) -> $crate::error::Result { + $crate::debugfs::attr::write(data, file, reader, offset, $is_signed) + } + } + }; +} + +#[macro_export] +macro_rules! attribute_unsigned { + ($attribute_type:ty, $fmt:literal) => { + $crate::debugfs::attribute!( + $attribute_type, + $fmt, + false, + $crate::debugfs::attr::_get_callback_unsigned::, + $crate::debugfs::attr::_set_callback_unsigned:: + ); + }; +} + +#[macro_export] +macro_rules! attribute_signed { + ($attribute_type:ty, $fmt:literal) => { + $crate::debugfs::attribute!( + $attribute_type, + $fmt, + true, + $crate::debugfs::attr::_get_callback_signed::, + $crate::debugfs::attr::_set_callback_signed:: + ); + }; +} + +pub use attribute; +pub use attribute_signed; +pub use attribute_unsigned; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 8a4e76ebbfbede..c3028997922095 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -36,6 +36,8 @@ extern crate self as kernel; mod allocator; mod build_assert; pub mod cred; +#[cfg(CONFIG_DEBUG_FS)] +pub mod debugfs; pub mod error; pub mod file; pub mod init; From e05f0677637b2816c5aef852b3e52b135b51ae7e Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Wed, 3 May 2023 01:01:08 +0200 Subject: [PATCH 18/18] samples: rust: add debugfs samples Add some basic debugfs sample module to show how to use the debugfs API. The sample module is showing: * How to create a directory * How to define and register a debugfs file by implementing file::Operations * How to use the attribute macro to expose simple numerical debugfs values. This is the equivalent of the C macro DEFINE_DEBUGFS_ATTRIBUTE{,_SIGNED} Signed-off-by: Fabien Parent --- samples/rust/Kconfig | 10 +++ samples/rust/Makefile | 1 + samples/rust/rust_debugfs.rs | 114 +++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 samples/rust/rust_debugfs.rs diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index b0f74a81c8f9ad..fc26dafc6fa386 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -10,6 +10,16 @@ menuconfig SAMPLES_RUST if SAMPLES_RUST +config SAMPLE_RUST_DEBUGFS + tristate "Debugfs" + help + This option builds the Rust debugfs module sample. + + To compile this as a module, choose M here: + the module will be called rust_debugfs. + + If unsure, say N. + config SAMPLE_RUST_MINIMAL tristate "Minimal" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 03086dabbea44f..f80ff73028b40c 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_SAMPLE_RUST_DEBUGFS) += rust_debugfs.o obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs new file mode 100644 index 00000000000000..063cfd21ea324c --- /dev/null +++ b/samples/rust/rust_debugfs.rs @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust debugfs device sample + +#![allow(missing_docs)] + +use kernel::{ + c_str, debugfs, file, + file::File, + io_buffer::IoBufferWriter, + prelude::*, + sync::{Arc, SpinLock}, + types::Mode, +}; + +struct SampleFile; + +#[vtable] +impl file::Operations for SampleFile { + fn open(_data: &(), _file: &File) -> Result { + Ok(()) + } + + fn read( + _data: (), + _file: &File, + writer: &mut impl IoBufferWriter, + offset: u64, + ) -> Result { + let data = b"Sample debugfs file implementing file::Operations\n"; + let offset = offset as usize; + + if offset > data.len() { + return Ok(0); + } + + let len = core::cmp::min(writer.len(), data.len() - offset); + writer.write_slice(&data[offset..(offset + len)])?; + Ok(len) + } +} + +#[pin_data] +struct IncAttribute { + #[pin] + data: SpinLock, +} + +impl debugfs::attr::Attribute for IncAttribute { + fn get(&self) -> Result { + let mut guard = self.data.lock(); + let ret = *guard; + *guard = ret + 1; + Ok(ret) + } + + fn set(&self, val: i64) -> Result { + let mut guard = self.data.lock(); + *guard = val; + Ok(()) + } +} + +debugfs::attribute_signed!(IncAttribute, "%#d\n"); + +struct RustDebugfs { + _sample_file: debugfs::PinnedRegistration, + _inc_attribute: debugfs::PinnedRegistration>, + _symlink: debugfs::Registration<()>, +} +impl kernel::Module for RustDebugfs { + fn init(_module: &'static ThisModule) -> Result { + let dir = Arc::try_new(debugfs::Registration::register_dir( + c_str!("rust_samples"), + None, + )?)?; + + let sample_file = debugfs::Registration::register_file::( + c_str!("sample"), + Mode::from_int(0444), + (), + Some(dir.clone()), + )?; + + let symlink = debugfs::Registration::register_symlink( + c_str!("sample_symlink"), + Some(dir.clone()), + c_str!("sample"), + )?; + + let attribute = Arc::pin_init(pin_init!(IncAttribute { + data <- kernel::new_spinlock!(0x42), + }))?; + let inc_attribute = attribute.register( + c_str!("inc_attribute"), + Mode::from_int(0666), + Some(dir.clone()), + )?; + + Ok(Self { + _sample_file: sample_file, + _inc_attribute: inc_attribute, + _symlink: symlink, + }) + } +} + +module! { + type: RustDebugfs, + name: "rust_debugfs", + author: "Fabien Parent ", + description: "Rust debugfs sample", + license: "GPL", +}