Merge branch 'clone_grant_using_fmap' into 'master'

Clone grant using fmap

See merge request redox-os/acid!3
This commit is contained in:
Jeremy Soller
2022-04-11 20:20:55 +00:00
2 changed files with 189 additions and 0 deletions

View File

@@ -0,0 +1,184 @@
use syscall::data::{Map, Packet};
use syscall::error::{Error, Result, EFAULT, EINVAL};
use syscall::flag::{CloneFlags, MapFlags, O_CREAT, O_RDONLY, O_RDWR, O_CLOEXEC, WaitFlags};
use syscall::scheme::SchemeMut;
// Start of code copied from syscall.
use std::convert::Infallible;
use syscall::{
clone,
close,
EIO,
exit,
pipe2,
read,
write,
};
#[must_use = "Daemon::ready must be called"]
pub struct Daemon {
write_pipe: usize,
}
impl Daemon {
pub fn new<F: FnOnce(Daemon) -> Infallible>(f: F) -> Result<u8> {
let mut pipes = [0; 2];
pipe2(&mut pipes, 0)?;
let [read_pipe, write_pipe] = pipes;
if unsafe { clone(CloneFlags::empty())? } == 0 {
let _ = close(read_pipe);
f(Daemon {
write_pipe,
});
// TODO: Replace Infallible with the never type once it is stabilized.
unreachable!();
} else {
let _ = close(write_pipe);
let mut data = [0];
let res = read(read_pipe, &mut data);
let _ = close(read_pipe);
if res? == 1 {
//exit(data[0] as usize)?;
//unreachable!();
Ok(data[0])
} else {
Err(Error::new(EIO))
}
}
}
pub fn ready(self) -> Result<()> {
let res = write(self.write_pipe, &[0]);
let _ = close(self.write_pipe);
if res? == 1 {
Ok(())
} else {
Err(Error::new(EIO))
}
}
}
// End of code copied from syscall
struct TestScheme(bool);
impl SchemeMut for TestScheme {
fn open(&mut self, _path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result<usize> { Ok(0) }
fn close(&mut self, _id: usize) -> Result<usize> { Ok(0) }
fn fmap(&mut self, id: usize, map: &Map) -> Result<usize> {
if map.size != PAGE_SIZE { return Err(Error::new(EINVAL)); }
let addr = unsafe { syscall::fmap(!0, &Map { offset: 0, size: PAGE_SIZE, flags: MapFlags::MAP_SHARED | MapFlags::PROT_WRITE, address: 0 })? };
if self.0 { unsafe { (addr as *mut u8).write(42); } }
Ok(addr)
}
}
const SCHEME_NAME: &str = "acid_clone_grant_using_fmap";
const PAGE_SIZE: usize = 4096;
fn inner(readonly: bool) -> Result<()> {
println!("Testing - {}", if readonly { "readonly" } else { "writable" });
Daemon::new(move |daemon: Daemon| -> std::convert::Infallible {
let e = |r| {
match r {
Ok(t) => t,
Err(e) => {
eprintln!("error in clone_grant_using_fmap daemon: {}", e);
std::process::exit(1);
}
}
};
let socket = e(syscall::open(format!(":{}{}", SCHEME_NAME, readonly), O_CREAT | O_RDWR | O_CLOEXEC));
daemon.ready();
let mut packet = Packet::default();
let mut scheme = TestScheme(readonly);
loop {
if e(syscall::read(socket, &mut packet)) == 0 { break };
scheme.handle(&mut packet);
if e(syscall::write(socket, &packet)) == 0 { break }
}
let _ = syscall::close(socket);
std::process::exit(0);
})?;
println!("Started scheme daemon");
let fd = syscall::open(format!("{}{}:", SCHEME_NAME, readonly), O_CLOEXEC | O_RDONLY)?;
let ptr = unsafe { syscall::fmap(fd, &Map { offset: 0, size: PAGE_SIZE, flags: MapFlags::MAP_PRIVATE | MapFlags::PROT_READ | MapFlags::PROT_WRITE, address: 0 })? as *mut u8 };
println!("Obtained pointer {:p}", ptr);
// TODO: Prevent optimizations which may cancel out this type of checking. Volatile will most
// likely be adequate.
if !readonly {
unsafe {
ptr.write_volatile(0x42);
}
}
let pid;
unsafe {
pid = syscall::clone(CloneFlags::empty())?;
// Keep in mind these two constants may disappear in the near future.
const PML4_SIZE: usize = 0x0000_0080_0000_0000;
const USER_TMP_GRANT_OFFSET: usize = 7 * PML4_SIZE;
const USER_GRANT_OFFSET: usize = 2 * PML4_SIZE;
assert!((ptr as usize) >= USER_GRANT_OFFSET);
assert!((ptr as usize) <= USER_TMP_GRANT_OFFSET);
// Make sure the temporary addresses are not left after the kernel has cloned.
assert_eq!(syscall::virttophys((ptr as usize) - USER_GRANT_OFFSET + USER_TMP_GRANT_OFFSET), Err(Error::new(EFAULT)));
println!("Old memory was correctly unmapped, for the {} process", if pid == 0 { "child" } else { "parent" });
if pid == 0 {
println!("Child process: checking...");
// We are the child process. Hopefully the kernel copied the grant properly and without
// aliasing.
if readonly {
assert_eq!(ptr.read_volatile(), 42);
} else {
assert_eq!(ptr.read_volatile(), 0);
ptr.write_volatile(0x43);
assert_eq!(ptr.read_volatile(), 0x43);
}
println!("Child process: obtained correct page");
std::process::exit(0);
}
}
println!("Waiting...");
syscall::waitpid(pid, &mut 0, WaitFlags::empty())?;
unsafe { assert_eq!(ptr.read_volatile(), if readonly { 42 } else { 0x42 }); }
println!("It worked!");
let _ = unsafe { syscall::funmap(ptr as usize, PAGE_SIZE) };
syscall::unlink(format!(":{}{}", SCHEME_NAME, readonly))?;
Ok(())
}
pub fn clone_grant_using_fmap() -> Result<(), String> {
inner(false).map_err(|e| e.to_string())?;
inner(true).map_err(|e| e.to_string())?;
Ok(())
}

View File

@@ -1,6 +1,8 @@
//!Acid testing program
#![feature(thread_local)]
mod clone_grant_using_fmap;
fn e<T, E: ToString>(error: Result<T, E>) -> Result<T, String> {
error.map_err(|e| e.to_string())
}
@@ -175,6 +177,8 @@ fn tls_test() -> Result<(), String> {
Ok(())
}
use self::clone_grant_using_fmap::*;
fn main() {
use std::collections::BTreeMap;
use std::{env, process};
@@ -187,6 +191,7 @@ fn main() {
tests.insert("tcp_fin", tcp_fin_test);
tests.insert("thread", thread_test);
tests.insert("tls", tls_test);
tests.insert("clone_grant_using_fmap", clone_grant_using_fmap);
let mut ran_test = false;
for arg in env::args().skip(1) {