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

Commit 9e7eba3

Browse files
committed
WIP: RCU
1 parent b355d71 commit 9e7eba3

File tree

9 files changed

+294
-0
lines changed

9 files changed

+294
-0
lines changed

build.rs

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const INCLUDED_VARS: &[&str] = &[
4545
"SEEK_CUR",
4646
"SEEK_END",
4747
"O_NONBLOCK",
48+
"init_task",
4849
];
4950
const OPAQUE_TYPES: &[&str] = &[
5051
// These need to be opaque because they're both packed and aligned, which rustc

src/bindings_helper.h

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <linux/fs.h>
33
#include <linux/module.h>
44
#include <linux/random.h>
5+
#include <linux/sched/task.h>
56
#include <linux/slab.h>
67
#include <linux/uaccess.h>
78
#include <linux/version.h>

src/helpers.c

+23
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#include <linux/bug.h>
22
#include <linux/printk.h>
3+
#include <linux/rcupdate.h>
34
#include <linux/uaccess.h>
45
#include <linux/version.h>
6+
#include <linux/sched/signal.h>
7+
#include <linux/sched/task.h>
58

69

710
int printk_helper(const unsigned char *s, int len)
@@ -23,6 +26,26 @@ int access_ok_helper(const void __user *addr, unsigned long n)
2326
#endif
2427
}
2528

29+
void rcu_read_lock_helper(void) {
30+
rcu_read_lock();
31+
}
32+
33+
void rcu_read_unlock_helper(void) {
34+
rcu_read_unlock();
35+
}
36+
37+
struct task_struct *next_task_helper(struct task_struct *p) {
38+
return next_task(p);
39+
}
40+
41+
void task_lock_helper(struct task_struct *p) {
42+
return task_lock(p);
43+
}
44+
45+
void task_unlock_helper(struct task_struct *p) {
46+
return task_unlock(p);
47+
}
48+
2649
/* see https://github.com/rust-lang/rust-bindgen/issues/1671 */
2750
_Static_assert(__builtin_types_compatible_p(size_t, uintptr_t),
2851
"size_t must match uintptr_t, what architecture is this??");

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ pub mod filesystem;
1515
pub mod printk;
1616
#[cfg(kernel_4_13_0_or_greater)]
1717
pub mod random;
18+
pub mod rcu;
19+
pub mod sched;
1820
pub mod sysctl;
1921
mod types;
2022
pub mod user_ptr;

src/rcu.rs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//! Bindings to RCU (read-copy-update), a high-performance lockless
2+
//! synchronization system used by many kernel data structures. At the
3+
//! moment, only calling functions that perform RCU reads is supported.
4+
5+
extern "C" {
6+
fn rcu_read_lock_helper();
7+
fn rcu_read_unlock_helper();
8+
}
9+
10+
/// A guard representing an RCU read-side critical section. Its
11+
/// constructor calls `rcu_read_lock()` and its destructor calls
12+
/// `rcu_read_unlock()`.
13+
///
14+
/// Within a read-side critical section (i.e., while at least one
15+
/// RcuReadGuard object is instantiated), objects behind RCU-protected
16+
/// pointers are guaranteed not to change, and so reading from them
17+
/// (after gaining a pointer with `rcu_dereference()`) is safe.
18+
///
19+
/// It is an error (risk of deadlock, but not memory unsafety) to block
20+
/// or schedule while holding an RcuReadGuard. It is also an error
21+
/// (guaranteed deadlock) to call `synchronize_rcu()` while holding an
22+
/// RcuReadGuard. Holding multiple guards (i.e., nesting read-side
23+
/// critical sections) is safe.
24+
pub struct RcuReadGuard(());
25+
26+
#[allow(clippy::new_without_default)]
27+
impl RcuReadGuard {
28+
pub fn new() -> Self {
29+
unsafe {
30+
rcu_read_lock_helper();
31+
}
32+
RcuReadGuard(())
33+
}
34+
}
35+
36+
impl Drop for RcuReadGuard {
37+
fn drop(&mut self) {
38+
unsafe {
39+
rcu_read_unlock_helper();
40+
}
41+
}
42+
}

src/sched.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//! APIs for interacting with the scheduler and with processes,
2+
//! corresponding to <linux/sched.h> and related header files.
3+
#![allow(improper_ctypes)]
4+
5+
use alloc::vec::Vec;
6+
use core::ptr;
7+
8+
use crate::bindings;
9+
use crate::rcu;
10+
11+
extern "C" {
12+
fn task_lock_helper(p: *mut bindings::task_struct);
13+
fn task_unlock_helper(p: *mut bindings::task_struct);
14+
fn next_task_helper(p: *mut bindings::task_struct) -> *mut bindings::task_struct;
15+
}
16+
17+
/// Represents a `struct task_struct *`.
18+
pub struct TaskStruct<'a>(&'a mut bindings::task_struct);
19+
20+
impl TaskStruct<'_> {
21+
/// Returns the command name / process title. This is a short name,
22+
/// typically the base name of the command, and does not have the
23+
/// full path or arguments. It's a fixed-sized set of bytes, but by
24+
/// convention it's interpreted as NUL-terminated.
25+
pub fn comm(&mut self) -> Vec<u8> {
26+
unsafe {
27+
task_lock_helper(self.0);
28+
// if only char were unsigned char
29+
let v = (&*(&(*self.0).comm[..] as *const _ as *const [u8])).to_vec();
30+
task_unlock_helper(self.0);
31+
v
32+
}
33+
}
34+
}
35+
36+
/// Iterate over every process on the system. Returns only processes,
37+
/// i.e., thread group leaders.
38+
///
39+
/// ```
40+
/// for p in each_process() {
41+
/// println!("{}", p.comm());
42+
/// }
43+
/// ```
44+
pub struct EachProcess<'g> {
45+
p: *mut bindings::task_struct,
46+
_g: &'g rcu::RcuReadGuard,
47+
}
48+
49+
pub fn each_process(g: &rcu::RcuReadGuard) -> EachProcess {
50+
// unsafe is bogus here because we don't read it
51+
// https://github.com/rust-lang/rust/issues/74843
52+
EachProcess {
53+
p: unsafe { &mut bindings::init_task },
54+
_g: g,
55+
}
56+
}
57+
58+
impl<'g> Iterator for EachProcess<'g> {
59+
type Item = TaskStruct<'g>;
60+
61+
fn next(&mut self) -> Option<TaskStruct<'g>> {
62+
// Safety:
63+
// - oldp is valid if not null, because it is either &init_task
64+
// (a static location) or updated by this function.
65+
// - next_task calls rcu_dereference internally, which is safe
66+
// because we hold self._g.
67+
// - Casting the returned pointer to &'g is safe because _g lives
68+
// for at least 'g.
69+
let oldp = self.p;
70+
if oldp.is_null() {
71+
return None;
72+
}
73+
self.p = unsafe { next_task_helper(self.p) };
74+
if self.p == unsafe { &mut bindings::init_task } {
75+
self.p = ptr::null_mut();
76+
}
77+
Some(TaskStruct(unsafe { &mut *oldp }))
78+
}
79+
}

tests/for-each-process/Cargo.toml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "for-each-process-tests"
3+
version = "0.1.0"
4+
authors = ["Alex Gaynor <[email protected]>", "Geoffrey Thomas <[email protected]>"]
5+
edition = "2018"
6+
7+
[lib]
8+
crate-type = ["staticlib"]
9+
test = false
10+
11+
[features]
12+
default = ["linux-kernel-module"]
13+
14+
[dependencies]
15+
linux-kernel-module = { path = "../..", optional = true }
16+
17+
[dev-dependencies]
18+
kernel-module-testlib = { path = "../../testlib" }
19+
libc = "0.2.58"
20+
tempfile = "3"

tests/for-each-process/src/lib.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#![no_std]
2+
3+
use linux_kernel_module::{self, cstr, rcu, sched};
4+
5+
struct PsAuxFile;
6+
7+
impl linux_kernel_module::file_operations::FileOperations for PsAuxFile {
8+
const VTABLE: linux_kernel_module::file_operations::FileOperationsVtable =
9+
linux_kernel_module::file_operations::FileOperationsVtable::builder::<Self>()
10+
.read()
11+
.build();
12+
13+
fn open() -> linux_kernel_module::KernelResult<Self> {
14+
Ok(PsAuxFile)
15+
}
16+
}
17+
18+
impl linux_kernel_module::file_operations::Read for PsAuxFile {
19+
fn read(
20+
&self,
21+
_file: &linux_kernel_module::file_operations::File,
22+
buf: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter,
23+
_offset: u64,
24+
) -> linux_kernel_module::KernelResult<()> {
25+
let g = rcu::RcuReadGuard::new();
26+
for mut p in sched::each_process(&g) {
27+
buf.write(&p.comm())?;
28+
buf.write(b"\n")?;
29+
}
30+
Ok(())
31+
}
32+
}
33+
struct ForEachProcessTestModule {
34+
_chrdev_registration: linux_kernel_module::chrdev::Registration,
35+
}
36+
37+
impl linux_kernel_module::KernelModule for ForEachProcessTestModule {
38+
fn init() -> linux_kernel_module::KernelResult<Self> {
39+
let chrdev_registration =
40+
linux_kernel_module::chrdev::builder(cstr!("foreachprocess-tests"), 0..1)?
41+
.register_device::<PsAuxFile>()
42+
.build()?;
43+
Ok(ForEachProcessTestModule {
44+
_chrdev_registration: chrdev_registration,
45+
})
46+
}
47+
}
48+
49+
linux_kernel_module::kernel_module!(
50+
ForEachProcessTestModule,
51+
author: b"Fish in a Barrel Contributors",
52+
description: b"A module for testing EachProcess",
53+
license: b"GPL"
54+
);

tests/for-each-process/tests/tests.rs

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use std::fs;
2+
use std::io::{self, BufRead};
3+
use std::path::PathBuf;
4+
use std::process::Command;
5+
6+
use libc;
7+
8+
use tempfile::TempDir;
9+
10+
use kernel_module_testlib::with_kernel_module;
11+
12+
fn get_device_major_number() -> libc::dev_t {
13+
let devices = fs::read_to_string("/proc/devices").unwrap();
14+
let dev_no_line = devices
15+
.lines()
16+
.find(|l| l.ends_with("foreachprocess-tests"))
17+
.unwrap();
18+
let elements = dev_no_line.rsplitn(2, " ").collect::<Vec<_>>();
19+
assert_eq!(elements.len(), 2);
20+
assert_eq!(elements[0], "foreachprocess-tests");
21+
return elements[1].trim().parse().unwrap();
22+
}
23+
24+
fn temporary_file_path() -> PathBuf {
25+
let mut p = TempDir::new().unwrap().into_path();
26+
p.push("device");
27+
return p;
28+
}
29+
30+
struct UnlinkOnDrop<'a> {
31+
path: &'a PathBuf,
32+
}
33+
34+
impl Drop for UnlinkOnDrop<'_> {
35+
fn drop(&mut self) {
36+
Command::new("sudo")
37+
.arg("rm")
38+
.arg(self.path.to_str().unwrap())
39+
.status()
40+
.unwrap();
41+
}
42+
}
43+
44+
fn mknod(path: &PathBuf, major: libc::dev_t, minor: libc::dev_t) -> UnlinkOnDrop {
45+
Command::new("sudo")
46+
.arg("mknod")
47+
.arg("--mode=a=rw")
48+
.arg(path.to_str().unwrap())
49+
.arg("c")
50+
.arg(major.to_string())
51+
.arg(minor.to_string())
52+
.status()
53+
.unwrap();
54+
return UnlinkOnDrop { path };
55+
}
56+
57+
#[test]
58+
fn test_running_systemd() {
59+
with_kernel_module(|| {
60+
let device_number = get_device_major_number();
61+
let p = temporary_file_path();
62+
let _u = mknod(&p, device_number, 0);
63+
64+
let f = fs::File::open(&p).unwrap();
65+
for line in io::BufReader::new(f).lines().take(5) {
66+
if dbg!(line).unwrap().starts_with("systemd\0") {
67+
return;
68+
}
69+
}
70+
panic!("what are you running, sysvinit?");
71+
});
72+
}

0 commit comments

Comments
 (0)