Safe Initialization for the Linux Kernel

Hello everyone,

I am currently working on improving the initialization ergonomics found in the Rust for Linux project. I have finished the general design and added essential comments. I would like to get some feedback on the approach, especially on the soundness, ergonomics and documentation.

Here is an example from the Rust-for-Linux project:

impl SharedState {
    fn try_new() -> Result<Ref<Self>> {
        let mut state = Pin::from(UniqueRef::try_new(Self {
            // SAFETY: `condvar_init!` is called below.
            state_changed: unsafe { CondVar::new() },
            // SAFETY: `mutex_init!` is called below.
            inner: unsafe { Mutex::new(SharedStateInner { token_count: 0 }) },
        })?);

        // SAFETY: `state_changed` is pinned when `state` is.
        let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.state_changed) };
        kernel::condvar_init!(pinned, "SharedState::state_changed");

        // SAFETY: `inner` is pinned when `state` is.
        let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.inner) };
        kernel::mutex_init!(pinned, "SharedState::inner");

        Ok(state.into())
    }
}

with my library:

impl SharedState {
    fn try_new() -> Result<Ref<Self>> {
        let state = Pin::from(UniqueRef::try_new(core::mem::MaybeUninit::<Self>::uninit())?);
        let state = kernel::init! {state => SharedState {
            kernel::init_with_lockdep!(.state_changed, "SharedState::state_changed");
            kernel::init_with_lockdep!(.inner, "SharedState::inner", SharedStateInner { token_count: 0 });
        }};
        Ok(state.into())
    }
}

Here is a small rundown of how the macro works:

  1. use raw pointers to initialize the fields of the struct
    • direct access is simple
    • function/macro calls get a special pointer (InitMe<'_, FieldType, Guard>) that has library support. The init! macro then expects a return type of InitProof<R, Guard> where R will be propagated outwards and Guard is a special guard parameter that was also present on the InitMe. It is essential for the soundness that an InitProof can only be constructed, if initialization was also successful. The guard parameter ensures that one cannot misuse a different InitProof to spoof the macro.
  2. use a struct initializer to ensure that each field was initialized exactly once
  3. "assume_init"

Thank you for your feedback!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.