rust/kernel/lib.rs | 1 + rust/kernel/safety.rs | 47 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 rust/kernel/safety.rs
Introduce a new `safety` module containing `unsafe_precondition_assert!`
macro. It is a wrapper around `debug_assert!`, intended for validating
pre-conditions of unsafe function.
When `CONFIG_RUST_DEBUG_ASSERTIONS` flag is enabled, this macro performs
runtime checks to ensure that the preconditions for unsafe function hold.
Otherwise, the macro is a no-op.
Suggested-by: Miguel Ojeda <ojeda@kernel.org>
Link: https://github.com/Rust-for-Linux/linux/issues/1162
Link: https://rust-for-linux.zulipchat.com/#narrow/channel/291566-Library/topic/.60unsafe_precondition_assert.60.20macro/with/528457452
Signed-off-by: Ritvik Gupta <ritvikfoss@gmail.com>
---
Changes in v3:
- Change doc example
- Link to v2: https://lore.kernel.org/all/20250730181420.6979b4f1@eugeo/T/#m9cd35a8fc02a18bd03934c7ecdcffe8667b5fbbd
Changes in v2:
- Wrap `debug_assert!` internally instead of using `pr_err!` with `assert!` + `cfg!(debug_assertions)
- Print “unsafe precondition(s) violated” only on assertion failure (no longer always printed)
- Use `# Safety` section instead of comment in the example
- Rename module-level doc
- Link to v1: https://lore.kernel.org/rust-for-linux/20250716045957.39732-1-ritvikfoss@gmail.com/
---
rust/kernel/lib.rs | 1 +
rust/kernel/safety.rs | 47 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 48 insertions(+)
create mode 100644 rust/kernel/safety.rs
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 11a6461e98da..7aab607dd879 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -104,6 +104,7 @@
pub mod print;
pub mod rbtree;
pub mod revocable;
+pub mod safety;
pub mod security;
pub mod seq_file;
pub mod sizes;
diff --git a/rust/kernel/safety.rs b/rust/kernel/safety.rs
new file mode 100644
index 000000000000..f591eed6da77
--- /dev/null
+++ b/rust/kernel/safety.rs
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Safety related APIs.
+
+/// Checks that preconditions of an unsafe function are followed.
+///
+/// The check is enabled at runtime if debug assertions (`CONFIG_RUST_DEBUG_ASSERTIONS`)
+/// are enabled. Otherwise, this macro is no-op.
+///
+/// # Examples
+///
+/// ```
+/// /// # Safety
+/// ///
+/// /// - `buf` must be non-null.
+/// /// - `buf` must be 16-byte aligned.
+/// /// - `len` must be multiple of [`PAGE_SIZE`].
+/// unsafe fn foo(buf: *const u8, len: usize) {
+/// unsafe_precondition_assert!(!buf.is_null(), "buf must not be null");
+/// unsafe_precondition_assert!((buf as usize) % 16 == 0, "buf must be 16-byte aligned");
+/// unsafe_precondition_assert!(
+/// len % PAGE_SIZE == 0,
+/// "len ({}) must be multiple of PAGE_SIZE ({})",
+/// len,
+/// PAGE_SIZE
+/// );
+/// // ...
+/// }
+/// ```
+///
+/// # Panics
+///
+/// Panics if the expression is evaluated to `false` at runtime.
+///
+#[macro_export]
+macro_rules! unsafe_precondition_assert {
+ ($cond:expr $(,)?) => {
+ $crate::unsafe_precondition_assert!(@inner $cond, ::core::stringify!($cond))
+ };
+
+ ($cond:expr, $($arg:tt)+) => {
+ $crate::unsafe_precondition_assert!(@inner $cond, ::core::format_args!($($arg)+))
+ };
+
+ (@inner $cond:expr, $msg:expr) => {
+ ::core::debug_assert!($cond, "unsafe precondition(s) violated: {}", $msg) };
+}
base-commit: dff64b072708ffef23c117fa1ee1ea59eb417807
--
2.50.1
Hi Ritvik, kernel test robot noticed the following build errors: [auto build test ERROR on dff64b072708ffef23c117fa1ee1ea59eb417807] url: https://github.com/intel-lab-lkp/linux/commits/Ritvik-Gupta/rust-kernel-introduce-unsafe_precondition_assert-macro/20250731-191444 base: dff64b072708ffef23c117fa1ee1ea59eb417807 patch link: https://lore.kernel.org/r/20250731111234.28602-1-ritvikfoss%40gmail.com patch subject: [PATCH v3] rust: kernel: introduce `unsafe_precondition_assert!` macro config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20250805/202508050756.oFg4cjPu-lkp@intel.com/config) compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261) rustc: rustc 1.88.0 (6b00bc388 2025-06-23) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250805/202508050756.oFg4cjPu-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202508050756.oFg4cjPu-lkp@intel.com/ All errors (new ones prefixed by >>): >> error: cannot find macro `unsafe_precondition_assert` in this scope --> rust/doctests_kernel_generated.rs:8012:5 | 8012 | unsafe_precondition_assert!(!buf.is_null(), "buf must not be null"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ::: /opt/cross/rustc-1.88.0-bindgen-0.72.0/rustup/toolchains/1.88.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ub_checks.rs:52:1 | 52 | macro_rules! assert_unsafe_precondition { | --------------------------------------- similarly named macro `assert_unsafe_precondition` defined here | help: a macro with a similar name exists | 8012 - unsafe_precondition_assert!(!buf.is_null(), "buf must not be null"); 8012 + assert_unsafe_precondition!(!buf.is_null(), "buf must not be null"); | help: consider importing this macro | 3 + use kernel::unsafe_precondition_assert; | -- >> error: cannot find macro `unsafe_precondition_assert` in this scope --> rust/doctests_kernel_generated.rs:8013:5 | 8013 | unsafe_precondition_assert!((buf as usize) % 16 == 0, "buf must be 16-byte aligned"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ::: /opt/cross/rustc-1.88.0-bindgen-0.72.0/rustup/toolchains/1.88.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ub_checks.rs:52:1 | 52 | macro_rules! assert_unsafe_precondition { | --------------------------------------- similarly named macro `assert_unsafe_precondition` defined here | help: a macro with a similar name exists | 8013 - unsafe_precondition_assert!((buf as usize) % 16 == 0, "buf must be 16-byte aligned"); 8013 + assert_unsafe_precondition!((buf as usize) % 16 == 0, "buf must be 16-byte aligned"); | help: consider importing this macro | 3 + use kernel::unsafe_precondition_assert; | -- >> error: cannot find macro `unsafe_precondition_assert` in this scope --> rust/doctests_kernel_generated.rs:8014:5 | 8014 | unsafe_precondition_assert!( | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ::: /opt/cross/rustc-1.88.0-bindgen-0.72.0/rustup/toolchains/1.88.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ub_checks.rs:52:1 | 52 | macro_rules! assert_unsafe_precondition { | --------------------------------------- similarly named macro `assert_unsafe_precondition` defined here | help: a macro with a similar name exists | 8014 - unsafe_precondition_assert!( 8014 + assert_unsafe_precondition!( | help: consider importing this macro | 3 + use kernel::unsafe_precondition_assert; | -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
On Thu, Jul 31, 2025 at 1:12 PM Ritvik Gupta <ritvikfoss@gmail.com> wrote: > > +/// /// - `buf` must be non-null. > +/// /// - `buf` must be 16-byte aligned. We don't know since the full body is not shown, but it is likely this would need to also be a valid pointer, i.e. it may be an uncommon example. Perhaps we could show one of the conditional cases, i.e. the "if `buf` is non-null, then it must be valid." cases. That could also be a nice excuse to also introduce an `implies()` function if an example allows for it. But we can do that later on, no worries. More importantly, could we have a user of the macro introduced in a second patch so that it gets already used? Thanks for the patch! Cheers, Miguel
On Thu, Jul 31, 2025 at 01:42:42PM +0200, Miguel Ojeda wrote: > On Thu, Jul 31, 2025 at 1:12 PM Ritvik Gupta <ritvikfoss@gmail.com> wrote: > > > > +/// /// - `buf` must be non-null. > > +/// /// - `buf` must be 16-byte aligned. > > We don't know since the full body is not shown, but it is likely this > would need to also be a valid pointer, i.e. it may be an uncommon > example. I believe this is a valid use-case for `unsafe_precondition_assert!`. Should we add similar example? ```cpu.rs /// Creates a new [`CpuId`] from the given `id` without checking bounds. /// /// # Safety /// /// The caller must ensure that `id` is a valid CPU ID (i.e., `0 <= id < nr_cpu_ids()`). #[inline] pub unsafe fn from_u32_unchecked(id: u32) -> Self { debug_assert!(id < nr_cpu_ids()); // Ensure the `id` fits in an [`i32`] as it's also representable that way. debug_assert!(id <= i32::MAX as u32); // INVARIANT: The function safety guarantees `id` is a valid CPU id. Self(id) } ``` > More importantly, could we have a user of the macro introduced in a > second patch so that it gets already used? I believe the `debug_assert!` calls inside the `unsafe fn` (excluding 'const fn' and 'CONFIG_RUST_OVERFLOW_CHECKS' flag) are the intended targets for `unsafe_precondition_assert!`. A quick grep (`git grep -B 15 -A 10 "debug_assert"`), I could find 6 relevant callers in `alloc/kvec.rs` (2) and `cpu.rs` (4), unless I'm missing something. I'll send another patch for this, after getting the example correct. Thanks for the feedback :)
© 2016 - 2025 Red Hat, Inc.