Skip to content

[Feature][Rust] Add Safe Rust Wrappers for Kernel IPC #11249

@0xcrax

Description

@0xcrax

With the recent merge of Rust tooling support (PR #11056, #10910), the foundation for Rust in RT-Thread has been laid. However, developers are currently forced to use unsafe blocks to interact with the kernel API.

This creates a gap in safety. RT-Thread is widely used in safety-critical domains (Automotive, Medical, Industrial). Standard C usage is prone to concurrency bugs (race conditions on SMP systems) and memory leaks. While the C kernel works, exposing it directly to Rust negates the safety guarantees that make Rust valuable.

A Safe Rust Wrapper solves this by enforcing memory safety and concurrency rules at compile-time, preventing data races and use-after-free errors before the code even runs.

Preferred Solution

I propose implementing a Safe Rust layer for the core IPC mechanisms (Mutex, Semaphore).

Key Design Features:

  1. RAII (Resource Acquisition Is Initialization): Resources are automatically released when objects go out of scope.
  2. Type Safety: Raw pointers from the C kernel are encapsulated.
  3. Concurrency Safety: The wrappers will use Rust's Send and Sync traits to prevent data races.

Proof of Concept (POC):
Here is a proposed design for a Safe Mutex wrapper:

use core::marker::PhantomData;
use core::ops::Deref;

// FFI: Bindings to C kernel functions
extern "C" {
    fn rt_mutex_create(name: *const i8, flag: u8) -> *mut rt_mutex;
    fn rt_mutex_take(mutex: *mut rt_mutex, time: i32) -> i32;
    fn rt_mutex_release(mutex: *mut rt_mutex) -> i32;
    fn rt_mutex_delete(mutex: *mut rt_mutex);
}

/// A Safe Wrapper for the RT-Thread Mutex
pub struct Mutex<T> {
    raw: *mut rt_mutex,
    _marker: PhantomData<T>,
}

impl<T> Mutex<T> {
    pub fn new(name: &str, t: T) -> Self {
        // ... CString conversion implementation ...
        let raw = unsafe { rt_mutex_create(cname.as_ptr(), 0) };
        Mutex { raw, _marker: PhantomData }
    }

    pub fn lock(&self) -> MutexGuard<'_, T> {
        unsafe { rt_mutex_take(self.raw, -1) }; // Wait forever
        MutexGuard { mutex: self, _marker: PhantomData }
    }
}

impl<T> Drop for Mutex<T> {
    fn drop(&mut self) {
        unsafe { rt_mutex_delete(self.raw) };
    }
}

pub struct MutexGuard<'a, T> {
    mutex: &'a Mutex<T>,
    _marker: PhantomData<T>,
}

impl<T> Drop for MutexGuard<'_, T> {
    fn drop(&mut self) {
        unsafe { rt_mutex_release(self.mutex.raw) };
    }
}

This design ensures that:

    1. rt_mutex_delete is called automatically (no memory leaks).
    1. rt_mutex_release is called automatically (no deadlocks if thread panics).
    1. The protected data can only be accessed through the guard.

Possible Alternatives

1. **Continue using `unsafe` FFI:** Developers can continue writing their own `unsafe` wrappers, but this leads to fragmented code and potential security risks across different projects.
2. **External Crate:** The wrapper could be maintained as a separate repository, but keeping it in the main tree ensures it stays synchronized with kernel API changes and improves the "out-of-the-box" experience for RT-Thread users.

Metadata

Metadata

Assignees

No one assigned

    Labels

    🎯 FocusShould focus on this issue/discussion/pr

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions