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:
parent
49ceae68a0
commit
7044dcff83
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user