Skip to content
This repository was archived by the owner on Mar 7, 2021. It is now read-only.

Fixes #13 -- added sysctls #17

Merged
merged 22 commits into from
Jun 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ cache:
- directories:
- static-filesystem/target/
- hello-world/target/
- example-sysctl/target/

branches:
only:
Expand All @@ -18,6 +19,7 @@ matrix:
include:
- env: MODULE_DIR=hello-world MODULE=helloworld
- env: MODULE_DIR=static-filesystem MODULE=staticfilesystem
- env: MODULE_DIR=example-sysctl MODULE=examplesysctl

install:
- sudo apt-get install -y "linux-headers-$(uname -r)"
Expand Down
10 changes: 10 additions & 0 deletions example-sysctl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "example-sysctl"
version = "0.1.0"
authors = ["Alex Gaynor <alex.gaynor@gmail.com>", "Geoffrey Thomas <geofft@ldpreload.com>"]

[lib]
crate-type = ["staticlib"]

[dependencies]
linux-kernel-module = { path = ".." }
9 changes: 9 additions & 0 deletions example-sysctl/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
obj-m := examplesysctl.o
examplesysctl-objs := target/x86_64-linux-kernel-module/debug/libexample_sysctl.a
EXTRA_LDFLAGS += --entry=init_module

all:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(CURDIR)

clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(CURDIR) clean
41 changes: 41 additions & 0 deletions example-sysctl/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#![no_std]

use core::sync::atomic::{AtomicBool, Ordering};

#[macro_use]
extern crate linux_kernel_module;

use linux_kernel_module::sysctl::Sysctl;
use linux_kernel_module::Mode;

struct ExampleSysctlModule {
a: Sysctl<AtomicBool>,
}

impl linux_kernel_module::KernelModule for ExampleSysctlModule {
fn init() -> linux_kernel_module::KernelResult<Self> {
let a = Sysctl::register(
"rust/example\x00",
"a\x00",
AtomicBool::new(false),
Mode::from_int(0o644),
)?;

Ok(ExampleSysctlModule { a })
}
}

impl Drop for ExampleSysctlModule {
fn drop(&mut self) {
println!(
"Current sysctl value: {}",
self.a.get().load(Ordering::Relaxed)
);
}
}
kernel_module!(
ExampleSysctlModule,
author: "Alex Gaynor and Geoffrey Thomas",
description: "A kernel module that offers a sysctl",
license: "GPL"
);
7 changes: 7 additions & 0 deletions src/helpers.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include <linux/bug.h>
#include <linux/printk.h>
#include <linux/uaccess.h>


int printk_helper(const unsigned char *s, int len)
{
Expand All @@ -10,3 +12,8 @@ void bug_helper(void)
{
BUG();
}

int access_ok_helper(unsigned int mode, const void __user *addr, unsigned long n)
{
return access_ok(mode, addr, n);
}
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![no_std]
#![feature(alloc, global_allocator, allocator_api, const_fn, lang_items, panic_implementation)]

#[macro_use]
extern crate alloc;
#[macro_use]
extern crate bitflags;
Expand All @@ -14,8 +15,12 @@ mod error;
pub mod filesystem;
#[macro_use]
pub mod printk;
pub mod sysctl;
mod types;
pub mod user_ptr;

pub use error::{Error, KernelResult};
pub use types::Mode;

pub type _InitResult = c_types::c_int;

Expand Down
153 changes: 153 additions & 0 deletions src/sysctl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use alloc::boxed::Box;
use core::mem;
use core::ptr;
use core::sync::atomic;

use bindings;
use c_types;
use error;
use types;
use user_ptr::{UserSlicePtr, UserSlicePtrWriter};

pub struct Sysctl<T: SysctlStorage> {
inner: Box<T>,
table: Box<[bindings::ctl_table]>,
header: *mut bindings::ctl_table_header,
}

pub trait SysctlStorage: Sync {
fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>);
fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult<()>);
}

fn trim_whitespace(mut data: &[u8]) -> &[u8] {
while !data.is_empty() && (data[0] == b' ' || data[0] == b'\t' || data[0] == b'\n') {
data = &data[1..];
}
while !data.is_empty()
&& (data[data.len() - 1] == b' '
|| data[data.len() - 1] == b'\t'
|| data[data.len() - 1] == b'\n')
{
data = &data[..data.len() - 1];
}
return data;
}

impl SysctlStorage for atomic::AtomicBool {
fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>) {
let result = match trim_whitespace(data) {
b"0" => {
self.store(false, atomic::Ordering::Relaxed);
Ok(())
}
b"1" => {
self.store(true, atomic::Ordering::Relaxed);
Ok(())
}
_ => Err(error::Error::EINVAL),
};
return (data.len(), result);
}

fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult<()>) {
let value = if self.load(atomic::Ordering::Relaxed) {
b"1\n"
} else {
b"0\n"
};
(value.len(), data.write(value))
}
}

unsafe extern "C" fn proc_handler<T: SysctlStorage>(
ctl: *mut bindings::ctl_table,
write: c_types::c_int,
buffer: *mut c_types::c_void,
len: *mut usize,
ppos: *mut bindings::loff_t,
) -> c_types::c_int {
// If we're reading from some offset other than the beginning of the file,
// return an empty read to signal EOF.
if *ppos != 0 && write == 0 {
*len = 0;
return 0;
}

let data = match UserSlicePtr::new(buffer, *len) {
Ok(ptr) => ptr,
Err(e) => return e.to_kernel_errno(),
};
let storage = &*((*ctl).data as *const T);
let (bytes_processed, result) = if write != 0 {
let data = match data.read_all() {
Ok(r) => r,
Err(e) => return e.to_kernel_errno(),
};
storage.store_value(&data)
} else {
let mut writer = data.writer();
storage.read_value(&mut writer)
};
*len = bytes_processed;
*ppos += *len as bindings::loff_t;
match result {
Ok(()) => 0,
Err(e) => e.to_kernel_errno(),
}
}

impl<T: SysctlStorage> Sysctl<T> {
pub fn register(
path: &'static str,
name: &'static str,
storage: T,
mode: types::Mode,
) -> error::KernelResult<Sysctl<T>> {
if !path.ends_with('\x00') || !name.ends_with('\x00') || name.contains('/') {
return Err(error::Error::EINVAL);
}

let storage = Box::new(storage);
let mut table = vec![
bindings::ctl_table {
procname: name.as_ptr() as *const i8,
mode: mode.as_int(),
data: &*storage as *const T as *mut c_types::c_void,
proc_handler: Some(proc_handler::<T>),

maxlen: 0,
child: ptr::null_mut(),
poll: ptr::null_mut(),
extra1: ptr::null_mut(),
extra2: ptr::null_mut(),
},
unsafe { mem::zeroed() },
].into_boxed_slice();

let result =
unsafe { bindings::register_sysctl(path.as_ptr() as *const i8, table.as_mut_ptr()) };
if result.is_null() {
return Err(error::Error::ENOMEM);
}

return Ok(Sysctl {
inner: storage,
table: table,
header: result,
});
}

pub fn get(&self) -> &T {
return &self.inner;
}
}

impl<T: SysctlStorage> Drop for Sysctl<T> {
fn drop(&mut self) {
unsafe {
bindings::unregister_sysctl_table(self.header);
}
self.header = ptr::null_mut();
}
}
13 changes: 13 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use bindings;

pub struct Mode(bindings::umode_t);

impl Mode {
pub fn from_int(m: u16) -> Mode {
Mode(m)
}

pub fn as_int(&self) -> u16 {
return self.0;
}
}
Loading