This repository has been archived by the owner on Mar 7, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
294 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
//! Bindings to RCU (read-copy-update), a high-performance lockless | ||
//! synchronization system used by many kernel data structures. At the | ||
//! moment, only calling functions that perform RCU reads is supported. | ||
|
||
extern "C" { | ||
fn rcu_read_lock_helper(); | ||
fn rcu_read_unlock_helper(); | ||
} | ||
|
||
/// A guard representing an RCU read-side critical section. Its | ||
/// constructor calls `rcu_read_lock()` and its destructor calls | ||
/// `rcu_read_unlock()`. | ||
/// | ||
/// Within a read-side critical section (i.e., while at least one | ||
/// RcuReadGuard object is instantiated), objects behind RCU-protected | ||
/// pointers are guaranteed not to change, and so reading from them | ||
/// (after gaining a pointer with `rcu_dereference()`) is safe. | ||
/// | ||
/// It is an error (risk of deadlock, but not memory unsafety) to block | ||
/// or schedule while holding an RcuReadGuard. It is also an error | ||
/// (guaranteed deadlock) to call `synchronize_rcu()` while holding an | ||
/// RcuReadGuard. Holding multiple guards (i.e., nesting read-side | ||
/// critical sections) is safe. | ||
pub struct RcuReadGuard(()); | ||
|
||
#[allow(clippy::new_without_default)] | ||
impl RcuReadGuard { | ||
pub fn new() -> Self { | ||
unsafe { | ||
rcu_read_lock_helper(); | ||
} | ||
RcuReadGuard(()) | ||
} | ||
} | ||
|
||
impl Drop for RcuReadGuard { | ||
fn drop(&mut self) { | ||
unsafe { | ||
rcu_read_unlock_helper(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
//! APIs for interacting with the scheduler and with processes, | ||
//! corresponding to <linux/sched.h> and related header files. | ||
#![allow(improper_ctypes)] | ||
|
||
use alloc::vec::Vec; | ||
use core::ptr; | ||
|
||
use crate::bindings; | ||
use crate::rcu; | ||
|
||
extern "C" { | ||
fn task_lock_helper(p: *mut bindings::task_struct); | ||
fn task_unlock_helper(p: *mut bindings::task_struct); | ||
fn next_task_helper(p: *mut bindings::task_struct) -> *mut bindings::task_struct; | ||
} | ||
|
||
/// Represents a `struct task_struct *`. | ||
pub struct TaskStruct<'a>(&'a mut bindings::task_struct); | ||
|
||
impl TaskStruct<'_> { | ||
/// Returns the command name / process title. This is a short name, | ||
/// typically the base name of the command, and does not have the | ||
/// full path or arguments. It's a fixed-sized set of bytes, but by | ||
/// convention it's interpreted as NUL-terminated. | ||
pub fn comm(&mut self) -> Vec<u8> { | ||
unsafe { | ||
task_lock_helper(self.0); | ||
// if only char were unsigned char | ||
let v = (&*(&(*self.0).comm[..] as *const _ as *const [u8])).to_vec(); | ||
task_unlock_helper(self.0); | ||
v | ||
} | ||
} | ||
} | ||
|
||
/// Iterate over every process on the system. Returns only processes, | ||
/// i.e., thread group leaders. | ||
/// | ||
/// ``` | ||
/// for p in each_process() { | ||
/// println!("{}", p.comm()); | ||
/// } | ||
/// ``` | ||
pub struct EachProcess<'g> { | ||
p: *mut bindings::task_struct, | ||
_g: &'g rcu::RcuReadGuard, | ||
} | ||
|
||
pub fn each_process(g: &rcu::RcuReadGuard) -> EachProcess { | ||
// unsafe is bogus here because we don't read it | ||
// https://github.com/rust-lang/rust/issues/74843 | ||
EachProcess { | ||
p: unsafe { &mut bindings::init_task }, | ||
_g: g, | ||
} | ||
} | ||
|
||
impl<'g> Iterator for EachProcess<'g> { | ||
type Item = TaskStruct<'g>; | ||
|
||
fn next(&mut self) -> Option<TaskStruct<'g>> { | ||
// Safety: | ||
// - oldp is valid if not null, because it is either &init_task | ||
// (a static location) or updated by this function. | ||
// - next_task calls rcu_dereference internally, which is safe | ||
// because we hold self._g. | ||
// - Casting the returned pointer to &'g is safe because _g lives | ||
// for at least 'g. | ||
let oldp = self.p; | ||
if oldp.is_null() { | ||
return None; | ||
} | ||
self.p = unsafe { next_task_helper(self.p) }; | ||
if self.p == unsafe { &mut bindings::init_task } { | ||
self.p = ptr::null_mut(); | ||
} | ||
Some(TaskStruct(unsafe { &mut *oldp })) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
[package] | ||
name = "for-each-process-tests" | ||
version = "0.1.0" | ||
authors = ["Alex Gaynor <[email protected]>", "Geoffrey Thomas <[email protected]>"] | ||
edition = "2018" | ||
|
||
[lib] | ||
crate-type = ["staticlib"] | ||
test = false | ||
|
||
[features] | ||
default = ["linux-kernel-module"] | ||
|
||
[dependencies] | ||
linux-kernel-module = { path = "../..", optional = true } | ||
|
||
[dev-dependencies] | ||
kernel-module-testlib = { path = "../../testlib" } | ||
libc = "0.2.58" | ||
tempfile = "3" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#![no_std] | ||
|
||
use linux_kernel_module::{self, cstr, rcu, sched}; | ||
|
||
struct PsAuxFile; | ||
|
||
impl linux_kernel_module::file_operations::FileOperations for PsAuxFile { | ||
const VTABLE: linux_kernel_module::file_operations::FileOperationsVtable = | ||
linux_kernel_module::file_operations::FileOperationsVtable::builder::<Self>() | ||
.read() | ||
.build(); | ||
|
||
fn open() -> linux_kernel_module::KernelResult<Self> { | ||
Ok(PsAuxFile) | ||
} | ||
} | ||
|
||
impl linux_kernel_module::file_operations::Read for PsAuxFile { | ||
fn read( | ||
&self, | ||
_file: &linux_kernel_module::file_operations::File, | ||
buf: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter, | ||
_offset: u64, | ||
) -> linux_kernel_module::KernelResult<()> { | ||
let g = rcu::RcuReadGuard::new(); | ||
for mut p in sched::each_process(&g) { | ||
buf.write(&p.comm())?; | ||
buf.write(b"\n")?; | ||
} | ||
Ok(()) | ||
} | ||
} | ||
struct ForEachProcessTestModule { | ||
_chrdev_registration: linux_kernel_module::chrdev::Registration, | ||
} | ||
|
||
impl linux_kernel_module::KernelModule for ForEachProcessTestModule { | ||
fn init() -> linux_kernel_module::KernelResult<Self> { | ||
let chrdev_registration = | ||
linux_kernel_module::chrdev::builder(cstr!("foreachprocess-tests"), 0..1)? | ||
.register_device::<PsAuxFile>() | ||
.build()?; | ||
Ok(ForEachProcessTestModule { | ||
_chrdev_registration: chrdev_registration, | ||
}) | ||
} | ||
} | ||
|
||
linux_kernel_module::kernel_module!( | ||
ForEachProcessTestModule, | ||
author: b"Fish in a Barrel Contributors", | ||
description: b"A module for testing EachProcess", | ||
license: b"GPL" | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
use std::fs; | ||
use std::io::{self, BufRead}; | ||
use std::path::PathBuf; | ||
use std::process::Command; | ||
|
||
use libc; | ||
|
||
use tempfile::TempDir; | ||
|
||
use kernel_module_testlib::with_kernel_module; | ||
|
||
fn get_device_major_number() -> libc::dev_t { | ||
let devices = fs::read_to_string("/proc/devices").unwrap(); | ||
let dev_no_line = devices | ||
.lines() | ||
.find(|l| l.ends_with("foreachprocess-tests")) | ||
.unwrap(); | ||
let elements = dev_no_line.rsplitn(2, " ").collect::<Vec<_>>(); | ||
assert_eq!(elements.len(), 2); | ||
assert_eq!(elements[0], "foreachprocess-tests"); | ||
return elements[1].trim().parse().unwrap(); | ||
} | ||
|
||
fn temporary_file_path() -> PathBuf { | ||
let mut p = TempDir::new().unwrap().into_path(); | ||
p.push("device"); | ||
return p; | ||
} | ||
|
||
struct UnlinkOnDrop<'a> { | ||
path: &'a PathBuf, | ||
} | ||
|
||
impl Drop for UnlinkOnDrop<'_> { | ||
fn drop(&mut self) { | ||
Command::new("sudo") | ||
.arg("rm") | ||
.arg(self.path.to_str().unwrap()) | ||
.status() | ||
.unwrap(); | ||
} | ||
} | ||
|
||
fn mknod(path: &PathBuf, major: libc::dev_t, minor: libc::dev_t) -> UnlinkOnDrop { | ||
Command::new("sudo") | ||
.arg("mknod") | ||
.arg("--mode=a=rw") | ||
.arg(path.to_str().unwrap()) | ||
.arg("c") | ||
.arg(major.to_string()) | ||
.arg(minor.to_string()) | ||
.status() | ||
.unwrap(); | ||
return UnlinkOnDrop { path }; | ||
} | ||
|
||
#[test] | ||
fn test_running_systemd() { | ||
with_kernel_module(|| { | ||
let device_number = get_device_major_number(); | ||
let p = temporary_file_path(); | ||
let _u = mknod(&p, device_number, 0); | ||
|
||
let f = fs::File::open(&p).unwrap(); | ||
for line in io::BufReader::new(f).lines().take(5) { | ||
if dbg!(line).unwrap().starts_with("systemd\0") { | ||
return; | ||
} | ||
} | ||
panic!("what are you running, sysvinit?"); | ||
}); | ||
} |