1 unstable release

0.1.0 Nov 26, 2018

#8 in #sysctl

631 stars & 24 watchers

5KB
98 lines

Writing Linux Kernel Module in Rust

Build Status

Device drivers on Linux-powered embedded or IoT systems execute in kernel space thus must be fully trusted. Any fault in drivers may significantly impact the whole system. However, third-party embedded hardware manufacturers usually ship their proprietary device drivers with their embedded devices. These out-of-tree device drivers are generally of poor quality because of a lack of code audit.

We propose an approach that helps third-party developers to improve the reliability and safety of device drivers without modifying the kernel: Rewriting device drivers in a memory-safe programming language called Rust. Rust's rigorous language model assists the device driver developers to detect many security issues at compile time. We designed a framework to help developers to quickly build device drivers in Rust. We also utilized Rust’s security features to provide several useful infrastructures for developers so that they can easily handle kernel memory allocation and concurrency management, at the same time, some common bugs (e.g. use-after-free) can be alleviated.

We demonstrate the generality of our framework by implementing a real-world device driver on Raspberry Pi 3, and our evaluation shows that device drivers generated by our framework have acceptable binary size for canonical embedded systems and the runtime overhead is negligible.

More details about the design and implementation can be found in our paper: Securing the Device Drivers of Your Embedded Systems: Framework and Prototype.

Requirements

Toolchain

  • x86_64

    Rust nightly. Tested on nightly-2018-09-30-x86_64-unknown-linux-gnu.

  • ARMv7 (Raspberry Pi)

    Rust nightly. In addition, you need to install the new target:

    $ rustup target add arm-unknown-linux-gnueabi
    

    And the arm-linux-gnueabihf- cross-compiler.

Linux Kernel Headers

A pre-built kernel (with configuration and header files) is needed.

  • x86_64

    Your Linux distribution should provide a package for this. For example, on Ubuntu, you can try:

    $ sudo apt-get install linux-headers-`uname -r`
    
  • ARMv7 (Raspberry Pi)

    You need to compile your own kernel in order for bindgen to work.

Build

  1. cargo-xbuild, rust-src and rustfmt-preview
$ cargo install cargo-xbuild
$ rustup component add --toolchain=nightly rust-src
$ rustup component add rustfmt-preview
  1. Select an example
$ cd hello_world
  1. Compile into a static library

    • x86_64
      $ RUST_TARGET_PATH=$(pwd)/.. cargo xbuild --target x86_64-linux-kernel-module
      
    • ARMv7 (Raspberry Pi)
      $ RUST_TARGET_PATH=$(pwd)/.. KDIR=<path-to-your-compiled-kernel> cargo xbuild --target armv7l-linux-kernel-module
      
  2. Link as a kernel module

    • x86_64
      $ make
      
    • ARMv7 (Raspberry Pi)
      $ make TARGET=armv7l-linux-kernel-module KDIR=<path-to-your-compiled-kernel> CROSS=arm-linux-gnueabihf-
      
  3. Load and test

    See below.

  4. If you want to clean it up

    • x86_64
      $ make clean;cargo clean
      
    • ARMv7 (Raspberry Pi)
      $ make clean TARGET=armv7l-linux-kernel-module KDIR=<path-to-your-compiled-kernel> CROSS=arm-linux-gnueabihf-;cargo clean
      

Load and Test

Examples are tested on Ubuntu 18.04 (Linux kernel 4.15.0-46-generic), smsc95xx is tested on Raspberry Pi 3 (Linux kernel 4.19.29).

hello_world

The simplest kernel module. It just prints "hello" and "goodbye".

$ sudo insmod helloworld.ko # load the module
$ sudo rmmod helloworld # remove the module
$ dmesg # dump kernel messages

yes_chardev

A simple character device which is similar to the yes Unix command.

$ sudo insmod yes_chardev.ko
$ cat /proc/devices # find the major number of the device 'yes', for example, 243
$ sudo mknod /dev/yes c 243 0 # make a filesystem node (replace 243 with your own major number)
$ sudo cat /dev/yes # read from the device
$ sudo rmmod yes_chardev

simple_sysctl

A simple sysctl device driver.

$ sudo insmod simple_sysctl.ko
$ cat /proc/sys/rust/example/test # the default value should be 1
$ sudo sh -c "echo 2 > /proc/sys/rust/example/test" # change the value
$ cat /proc/sys/rust/example/test # now the value is 2
$ sudo rmmod simple_sysctl

There is another way to read/write the sysctl value:

$ sysctl rust.example.test # read
$ sudo sysctl -w rust.example.test=2 # write

sync_example

A simple example to illustrate the use of Spinlock and Mutex.

let mutex_data = sync::Mutex::new(50);
let mut data = mutex_data.lock();
println!("Data {} is locked by a mutex", *data);
*data = 100;
println!("Now data is {}", *data);
println!("Hello from Rust!");

The above code snippet will output like this:

[  424.328154] Mutex is locked!
[  424.328156] Data 50 is locked by a mutex
[  424.328158] Now data is 100
[  424.328158] Hello from Rust!
[  424.328160] Mutex is dropped!

smsc95xx

A highly simplified real-world device driver for LAN9512 USB to Ethernet controller, which is used on Raspberry Pi 3. The implementation resembles the C version.

Roadmap

The efforts of writing kernel modules in Rust can be traced back to 2013 (the first commit of rust.ko), long before Rust's first stable version was released. Here we list some of the objectives that people have already achieved and what we plan to achieve in the future.

  • Generate OS-independent machine code by using JSON specification files
  • Recompile pre-compiled libraries (core, compiler_builtins, alloc) by using cargo-xbuild
  • Generate Rust bindings for kernel headers to reuse existing data structures and functions defined inside the kernel by using bindgen.
  • Kernel allocator by using the GlobalAlloc trait.
  • Kernel synchronizations by reimplementing the synchronization primitives.
  • A simple real-world device driver for LAN9512.
  • Minimize the use of unsafe Rust.
  • Find a idiomatic way to define callback functions.
  • Failure recovery.
  • Use static analysis tool to reason about unsafe Rust code.

Acknowledgment

Thanks to these previous works on writing Linux kernel driver in Rust. Their attempts inspire us a lot.

License

GPL-2.0

Dependencies