1

rust: macros: fix soundness issue in module! macro

The `module!` macro creates glue code that are called by C to initialize
the Rust modules using the `Module::init` function. Part of this glue
code are the local functions `__init` and `__exit` that are used to
initialize/destroy the Rust module.

These functions are safe and also visible to the Rust mod in which the
`module!` macro is invoked. This means that they can be called by other
safe Rust code. But since they contain `unsafe` blocks that rely on only
being called at the right time, this is a soundness issue.

Wrap these generated functions inside of two private modules, this
guarantees that the public functions cannot be called from the outside.
Make the safe functions `unsafe` and add SAFETY comments.

Cc: stable@vger.kernel.org
Reported-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Closes: https://github.com/Rust-for-Linux/linux/issues/629
Fixes: 1fbde52bde ("rust: add `macros` crate")
Signed-off-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Wedson Almeida Filho <walmeida@microsoft.com>
Link: https://lore.kernel.org/r/20240401185222.12015-1-benno.lossin@proton.me
[ Moved `THIS_MODULE` out of the private-in-private modules since it
  should remain public, as Dirk Behme noticed [1]. Capitalized comments,
  avoided newline in non-list SAFETY comments and reworded to add
  Reported-by and newline. ]
Link: https://rust-for-linux.zulipchat.com/#narrow/stream/291565-Help/topic/x/near/433512583 [1]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
Benno Lossin 2024-04-01 18:52:50 +00:00 committed by Miguel Ojeda
parent 49ceae68a0
commit 7044dcff83

View File

@ -199,17 +199,6 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
/// Used by the printing macros, e.g. [`info!`]. /// Used by the printing macros, e.g. [`info!`].
const __LOG_PREFIX: &[u8] = b\"{name}\\0\"; const __LOG_PREFIX: &[u8] = b\"{name}\\0\";
/// The \"Rust loadable module\" mark.
//
// This may be best done another way later on, e.g. as a new modinfo
// key or a new section. For the moment, keep it simple.
#[cfg(MODULE)]
#[doc(hidden)]
#[used]
static __IS_RUST_MODULE: () = ();
static mut __MOD: Option<{type_}> = None;
// SAFETY: `__this_module` is constructed by the kernel at load time and will not be // SAFETY: `__this_module` is constructed by the kernel at load time and will not be
// freed until the module is unloaded. // freed until the module is unloaded.
#[cfg(MODULE)] #[cfg(MODULE)]
@ -221,81 +210,132 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
kernel::ThisModule::from_ptr(core::ptr::null_mut()) kernel::ThisModule::from_ptr(core::ptr::null_mut())
}}; }};
// Loadable modules need to export the `{{init,cleanup}}_module` identifiers. // Double nested modules, since then nobody can access the public items inside.
/// # Safety mod __module_init {{
/// mod __module_init {{
/// This function must not be called after module initialization, because it may be use super::super::{type_};
/// freed after that completes.
#[cfg(MODULE)]
#[doc(hidden)]
#[no_mangle]
#[link_section = \".init.text\"]
pub unsafe extern \"C\" fn init_module() -> core::ffi::c_int {{
__init()
}}
#[cfg(MODULE)] /// The \"Rust loadable module\" mark.
#[doc(hidden)] //
#[no_mangle] // This may be best done another way later on, e.g. as a new modinfo
pub extern \"C\" fn cleanup_module() {{ // key or a new section. For the moment, keep it simple.
__exit() #[cfg(MODULE)]
}} #[doc(hidden)]
#[used]
static __IS_RUST_MODULE: () = ();
// Built-in modules are initialized through an initcall pointer static mut __MOD: Option<{type_}> = None;
// and the identifiers need to be unique.
#[cfg(not(MODULE))]
#[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))]
#[doc(hidden)]
#[link_section = \"{initcall_section}\"]
#[used]
pub static __{name}_initcall: extern \"C\" fn() -> core::ffi::c_int = __{name}_init;
#[cfg(not(MODULE))] // Loadable modules need to export the `{{init,cleanup}}_module` identifiers.
#[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] /// # Safety
core::arch::global_asm!( ///
r#\".section \"{initcall_section}\", \"a\" /// This function must not be called after module initialization, because it may be
__{name}_initcall: /// freed after that completes.
.long __{name}_init - . #[cfg(MODULE)]
.previous #[doc(hidden)]
\"# #[no_mangle]
); #[link_section = \".init.text\"]
pub unsafe extern \"C\" fn init_module() -> core::ffi::c_int {{
// SAFETY: This function is inaccessible to the outside due to the double
// module wrapping it. It is called exactly once by the C side via its
// unique name.
unsafe {{ __init() }}
}}
#[cfg(not(MODULE))] #[cfg(MODULE)]
#[doc(hidden)] #[doc(hidden)]
#[no_mangle] #[no_mangle]
pub extern \"C\" fn __{name}_init() -> core::ffi::c_int {{ pub extern \"C\" fn cleanup_module() {{
__init() // SAFETY:
}} // - This function is inaccessible to the outside due to the double
// module wrapping it. It is called exactly once by the C side via its
// unique name,
// - furthermore it is only called after `init_module` has returned `0`
// (which delegates to `__init`).
unsafe {{ __exit() }}
}}
#[cfg(not(MODULE))] // Built-in modules are initialized through an initcall pointer
#[doc(hidden)] // and the identifiers need to be unique.
#[no_mangle] #[cfg(not(MODULE))]
pub extern \"C\" fn __{name}_exit() {{ #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))]
__exit() #[doc(hidden)]
}} #[link_section = \"{initcall_section}\"]
#[used]
pub static __{name}_initcall: extern \"C\" fn() -> core::ffi::c_int = __{name}_init;
fn __init() -> core::ffi::c_int {{ #[cfg(not(MODULE))]
match <{type_} as kernel::Module>::init(&THIS_MODULE) {{ #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)]
Ok(m) => {{ core::arch::global_asm!(
unsafe {{ r#\".section \"{initcall_section}\", \"a\"
__MOD = Some(m); __{name}_initcall:
.long __{name}_init - .
.previous
\"#
);
#[cfg(not(MODULE))]
#[doc(hidden)]
#[no_mangle]
pub extern \"C\" fn __{name}_init() -> core::ffi::c_int {{
// SAFETY: This function is inaccessible to the outside due to the double
// module wrapping it. It is called exactly once by the C side via its
// placement above in the initcall section.
unsafe {{ __init() }}
}}
#[cfg(not(MODULE))]
#[doc(hidden)]
#[no_mangle]
pub extern \"C\" fn __{name}_exit() {{
// SAFETY:
// - This function is inaccessible to the outside due to the double
// module wrapping it. It is called exactly once by the C side via its
// unique name,
// - furthermore it is only called after `__{name}_init` has returned `0`
// (which delegates to `__init`).
unsafe {{ __exit() }}
}}
/// # Safety
///
/// This function must only be called once.
unsafe fn __init() -> core::ffi::c_int {{
match <{type_} as kernel::Module>::init(&super::super::THIS_MODULE) {{
Ok(m) => {{
// SAFETY: No data race, since `__MOD` can only be accessed by this
// module and there only `__init` and `__exit` access it. These
// functions are only called once and `__exit` cannot be called
// before or during `__init`.
unsafe {{
__MOD = Some(m);
}}
return 0;
}}
Err(e) => {{
return e.to_errno();
}}
}} }}
return 0;
}} }}
Err(e) => {{
return e.to_errno(); /// # Safety
///
/// This function must
/// - only be called once,
/// - be called after `__init` has been called and returned `0`.
unsafe fn __exit() {{
// SAFETY: No data race, since `__MOD` can only be accessed by this module
// and there only `__init` and `__exit` access it. These functions are only
// called once and `__init` was already called.
unsafe {{
// Invokes `drop()` on `__MOD`, which should be used for cleanup.
__MOD = None;
}}
}} }}
{modinfo}
}} }}
}} }}
fn __exit() {{
unsafe {{
// Invokes `drop()` on `__MOD`, which should be used for cleanup.
__MOD = None;
}}
}}
{modinfo}
", ",
type_ = info.type_, type_ = info.type_,
name = info.name, name = info.name,