rust: kernel: introduce `unsafe_precondition_assert!` macro
PATCH v4 1/1 rust: kernel: introduce `unsafe_precondition_assert!` macro
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.
---
Changes in v4:
- Change doc example
- Add no_run attribute to the doc example
- Link to v3: https://lore.kernel.org/rust-for-linux/[email protected]/
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/[email protected]/
--- rust/kernel/lib.rs | 1 + rust/kernel/safety.rs | 48 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 rust/kernel/safety.rs
rust/kernel/lib.rs +1 -0
@@ -113,6 +113,7 @@pub mod rbtree;pub mod regulator;pub mod revocable; +pub mod safety;pub mod security;pub mod seq_file;pub mod sizes;
rust/kernel/safety.rs new file +48 -0
48 lines not on this page · raw message
base-commit: 37816488247ddddbc3de113c78c83572274b1e2e
Signature · 1 line
2.50.1
Alice Ryhl
reviewerQuoted 13 lines · Ritvik Gupta
Introduce a new
safetymodule containingunsafe_precondition_assert!macro. It is a wrapper arounddebug_assert!, intended for validating pre-conditions of unsafe function.When
CONFIG_RUST_DEBUG_ASSERTIONSflag is enabled, this macro performs runtime checks to ensure that the preconditions for unsafe function hold. Otherwise, the macro is a no-op.
rust/kernel/safety.rs line 43
Quoted 2 lines
rust/kernel/safety.rs new file +2
-0+ ($cond:expr, $($arg:tt)+) => { + $crate::unsafe_precondition_assert!(@inner $cond, ::core::format_args!($($arg)+))
Tamir, taking your fmt series into account, what's the correct replacement for ::core::format_args! here?
Alice
Quoted 5 lines
rust/kernel/safety.rs line 43
Quoted 2 lines
rust/kernel/safety.rs new file +2
-0+ ($cond:expr, $($arg:tt)+) => { + $crate::unsafe_precondition_assert!(@inner $cond, ::core::format_args!($($arg)+))Tamir, taking your fmt series into account, what's the correct replacement for ::core::format_args! here?
Whoops! I wasn't aware of the fmt-series! Thanks for mentioning it :)
After a quick search, kernel::prelude::fmt! type/macro [1,2] seems to be the relevant replacement. So, the line would look like:
`$crate::unsafe_precondition_assert!(@inner $cond, $crate::prelude::fmt!($($arg)+))`
Does that sound right?
[1]: https://github.com/Rust-for-Linux/linux/blob/8f5ae30d69d7543eee0d70083daf4de8fe15d585/rust/kernel/prelude.rs#L36
[2]: https://lore.kernel.org/rust-for-linux/[email protected]/
kernel test robot
build botkernel test robot noticed the following build errors:
Hi Ritvik,
kernel test robot noticed the following build errors:
[auto build test ERROR on 37816488247ddddbc3de113c78c83572274b1e2e]
url: https://github.com/intel-lab-lkp/linux/commits/Ritvik-Gupta/rust-kernel-introduce-unsafe_precondition_assert-macro/20250809-032122 base: 37816488247ddddbc3de113c78c83572274b1e2e patch link: https://lore.kernel.org/r/20250808192005.209188-1-ritvikfoss%40gmail.com patch subject: [PATCH v4] rust: kernel: introduce `unsafe_precondition_assert!` macro config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20250812/[email protected]/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/20250812/[email protected]/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 <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
>> error[E0423]: cannot initialize a tuple struct which contains private fields
--> rust/doctests_kernel_generated.rs:8841:5
|
8841 | CpuId(id)
| ^^^^^
|
note: constructor is not visible here due to private fields --> rust/kernel/cpu.rs:47:18
|
47 | pub struct CpuId(u32);
| ^^^ private field
help: you might have meant to use an associated function to build this type |
8841 - CpuId(id)
8841 + CpuId::from_i32_unchecked(_)
|
8841 - CpuId(id)
8841 + CpuId::from_u32_unchecked(_)
|
8841 - CpuId(id)
8841 + CpuId::current()
|
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
If someone's referring to it in the future, this was doctest issue.
It’s actually the second time I’ve hit it.
On my end, this error wasn't occurring when compiling the kernel.
What I was missing:
1. Enable Kernel Hacking > Kernel Testing and Coverage > KUnit
2. Enable Doctest in Rust hacking > Doctests for the 'kernel' crate
I can now reproduce capture the issue locally.
Won't happen again :)
Quoted 12 lines
rust/kernel/safety.rs new file +12
-0+/// # Examples +/// +/// ```no_run +/// # use kernel::unsafe_precondition_assert; +/// # use kernel::cpu::{nr_cpu_ids, CpuId}; +/// /// Creates a [`CpuId`] from the given `id` without bound checks. +/// /// +/// /// # Safety +/// /// +/// /// The caller must ensure that `id` is a valid CPU ID (i.e, `0 <= id < nr_cpu_ids()`). +/// unsafe fn new_cpu_id_unchecked(id: i32) -> CpuId { +/// let max_cpus = nr_cpu_ids();
rust/kernel/safety.rs line 31
Quoted 10 lines
rust/kernel/safety.rs new file +10
-0+/// +/// unsafe_precondition_assert!(id >= 0, "id ({}) must be positive", id); +/// +/// unsafe_precondition_assert!( +/// id < max_cpus, "id ({}) must be less than total CPUs ({})", id, max_cpus +/// ); +/// +/// CpuId(id) +/// } +/// ```
The example I've included is incorrect because CpuId inner field is private, so it can't be constructed this way.
CpuId::from_i32_unchecked() exists, using it here would make the example a trivial wrapper around that method, that would be unnecessary and confusing. Also, I wasn't sure about the doctest config when I wrote it.
I'll send v5 with correct and much better example. Plz ignore it. Sorry for the noise.