Add support for /proc/self(pid)/maps

This commit is contained in:
Hui, Chunyang 2022-08-02 02:47:36 +00:00 committed by volcano
parent dd295c1391
commit d1acb84362
15 changed files with 379 additions and 126 deletions

@ -266,6 +266,10 @@ impl File for INodeFile {
}
impl INodeFile {
pub fn inode(&self) -> &Arc<dyn INode> {
&self.inode
}
pub fn open(inode: Arc<dyn INode>, abs_path: &str, flags: u32) -> Result<Self> {
let access_mode = AccessMode::from_u32(flags)?;
if (access_mode.readable() && !inode.allow_read()?) {

@ -0,0 +1,134 @@
use super::*;
use crate::vm::{ChunkType, VMArea, VMPerms, VMRange};
// This file is to implement /proc/self(pid)/maps file system.
//
// Print format:
// vmrange_start-vmrange_end, permission, shared/private, offset, device ID, inode, pathname
//
// Example:
// - cat /proc/self/maps
// 555555554000-555555556000 r--p 00000000 08:12 39321752 /usr/bin/cat
// 555555556000-55555555b000 r-xp 00002000 08:12 39321752 /usr/bin/cat
// 55555555b000-55555555e000 r--p 00007000 08:12 39321752 /usr/bin/cat
// 55555555e000-55555555f000 r--p 00009000 08:12 39321752 /usr/bin/cat
// 55555555f000-555555560000 rw-p 0000a000 08:12 39321752 /usr/bin/cat
// 555555560000-555555581000 rw-p 00000000 00:00 0 [heap]
// 7ffff7536000-7ffff7558000 rw-p 00000000 00:00 0
// 7ffff7558000-7ffff7dc8000 r--p 00000000 08:12 39322175 /usr/lib/locale/locale-archive
// 7ffff7dc8000-7ffff7dea000 r--p 00000000 08:12 39324754 /usr/lib/x86_64-linux-gnu/libc-2.31.so
// 7ffff7dea000-7ffff7f62000 r-xp 00022000 08:12 39324754 /usr/lib/x86_64-linux-gnu/libc-2.31.so
// 7ffff7f62000-7ffff7fb0000 r--p 0019a000 08:12 39324754 /usr/lib/x86_64-linux-gnu/libc-2.31.so
// 7ffff7fb0000-7ffff7fb4000 r--p 001e7000 08:12 39324754 /usr/lib/x86_64-linux-gnu/libc-2.31.so
// 7ffff7fb4000-7ffff7fb6000 rw-p 001eb000 08:12 39324754 /usr/lib/x86_64-linux-gnu/libc-2.31.so
// 7ffff7fb6000-7ffff7fbc000 rw-p 00000000 00:00 0
// 7ffff7fcf000-7ffff7fd0000 r--p 00000000 08:12 39324750 /usr/lib/x86_64-linux-gnu/ld-2.31.so
// 7ffff7fd0000-7ffff7ff3000 r-xp 00001000 08:12 39324750 /usr/lib/x86_64-linux-gnu/ld-2.31.so
// 7ffff7ff3000-7ffff7ffb000 r--p 00024000 08:12 39324750 /usr/lib/x86_64-linux-gnu/ld-2.31.so
// 7ffff7ffc000-7ffff7ffd000 r--p 0002c000 08:12 39324750 /usr/lib/x86_64-linux-gnu/ld-2.31.so
// 7ffff7ffd000-7ffff7ffe000 rw-p 0002d000 08:12 39324750 /usr/lib/x86_64-linux-gnu/ld-2.31.so
// 7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
// 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
// 80000006b000-80000006f000 r--p 00000000 00:00 0 [vvar]
// 80000006f000-800000071000 r-xp 00000000 00:00 0 [vdso]
// ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
//
// Known limitation:
// - Device ID is not provided by FS
// - Not shown in address order
pub struct ProcMapsINode(ProcessRef);
impl ProcMapsINode {
pub fn new(process_ref: &ProcessRef) -> Arc<dyn INode> {
Arc::new(File::new(Self(Arc::clone(process_ref))))
}
}
impl ProcINode for ProcMapsINode {
fn generate_data_in_bytes(&self) -> vfs::Result<Vec<u8>> {
let result_string = {
let main_thread = self.0.main_thread().unwrap();
let process_vm = main_thread.vm();
let heap_range = process_vm.heap_range();
let stack_range = process_vm.stack_range();
let process_vm_chunks = process_vm.mem_chunks().read().unwrap();
process_vm_chunks
.iter()
.map(|chunk| match chunk.internal() {
ChunkType::SingleVMA(vma) => {
let range = chunk.range();
let heap_or_stack = if range == heap_range {
Some(" [heap]")
} else if range == stack_range {
Some(" [stack]")
} else {
None
};
let vma = vma.lock().unwrap();
get_output_for_vma(&vma, heap_or_stack)
}
ChunkType::MultiVMA(internal_manager) => {
let internal = internal_manager.lock().unwrap();
let vmas_list = internal.chunk_manager().vmas();
vmas_list
.iter()
.map(|obj| get_output_for_vma(obj.vma(), None))
.fold(String::new(), |acc, vma_info| acc + &vma_info)
}
})
.fold(String::new(), |acc, vma_info| acc + &vma_info)
};
Ok(result_string.into_bytes())
}
}
fn get_output_for_vma(vma: &VMArea, heap_or_stack: Option<&str>) -> String {
let range = vma.range();
let perms = vma.perms();
let (file_path, offset, device_id, inode_num) = {
if let Some((file, offset)) = vma.init_file() {
let inode_file = file.as_inode_file().unwrap();
let file_path = inode_file.abs_path();
let inode_num = inode_file.inode().metadata().unwrap().inode;
let device_id = inode_file.inode().metadata().unwrap().dev;
(file_path, offset, device_id, inode_num)
} else if heap_or_stack.is_some() {
(heap_or_stack.unwrap(), 0, 0, 0)
} else {
("", 0, 0, 0)
}
};
let shared = vma.writeback_file().is_some();
print_each_map(
range, perms, shared, offset, device_id, inode_num, file_path,
)
}
fn print_each_map(
range: &VMRange,
perms: VMPerms,
shared: bool,
offset: usize,
device_id: usize,
inode_num: usize,
file_path: &str,
) -> String {
let result_str = format!(
"{:x}-{:x} {}{} {:08x} {} {} {}\n",
range.start(),
range.end(),
perms.display(),
if shared { "s" } else { "p" },
offset,
device_id,
inode_num,
file_path
);
result_str
}

@ -7,6 +7,7 @@ use self::comm::ProcCommINode;
use self::cwd::ProcCwdSymINode;
use self::exe::ProcExeSymINode;
use self::fd::LockedProcFdDirINode;
use self::maps::ProcMapsINode;
use self::root::ProcRootSymINode;
use self::stat::ProcStatINode;
@ -15,6 +16,7 @@ mod comm;
mod cwd;
mod exe;
mod fd;
mod maps;
mod root;
mod stat;
@ -60,6 +62,9 @@ impl LockedPidDirINode {
// stat
let stat_inode = ProcStatINode::new(&file.process_ref);
file.entries.insert(String::from("stat"), stat_inode);
// maps
let maps_inode = ProcMapsINode::new(&file.process_ref);
file.entries.insert(String::from("maps"), maps_inode);
Ok(())
}

@ -1,6 +1,6 @@
use super::super::elf_file::*;
use super::ThreadRef;
use crate::fs::{FileMode, INodeExt};
use crate::fs::{AsINodeFile, FileMode, INodeExt};
use crate::prelude::*;
use rcore_fs::vfs::{FileType, INode, Metadata};
use std::ffi::CString;
@ -13,10 +13,10 @@ use std::ffi::CString;
pub fn load_exec_file_hdr_to_vec(
file_path: &str,
current_ref: &ThreadRef,
) -> Result<(Option<String>, Arc<dyn INode>, Vec<u8>, ElfHeader)> {
let (inode, file_buf, elf_hdr) = load_file_hdr_to_vec(&file_path, current_ref)?;
) -> Result<(Option<String>, FileRef, Vec<u8>, ElfHeader)> {
let (file_ref, file_buf, elf_hdr) = load_file_hdr_to_vec(&file_path, current_ref)?;
if elf_hdr.is_some() {
Ok((None, inode, file_buf, elf_hdr.unwrap()))
Ok((None, file_ref, file_buf, elf_hdr.unwrap()))
} else {
// loaded file is not Elf format, try script file
if !is_script_file(&file_buf) {
@ -30,14 +30,14 @@ pub fn load_exec_file_hdr_to_vec(
"libos doesn't support executing binaries from \"/host\" directory"
);
}
let (interp_inode, interp_buf, interp_hdr) =
let (interp_file, interp_buf, interp_hdr) =
load_file_hdr_to_vec(&interpreter_path, current_ref)?;
let interp_hdr = if interp_hdr.is_none() {
return_errno!(ENOEXEC, "scrip interpreter is not ELF format");
} else {
interp_hdr.unwrap()
};
Ok((Some(interpreter_path), interp_inode, interp_buf, interp_hdr))
Ok((Some(interpreter_path), interp_file, interp_buf, interp_hdr))
}
}
@ -72,13 +72,13 @@ fn parse_script_interpreter(file_buf: &Vec<u8>) -> Result<String> {
pub fn load_file_hdr_to_vec(
file_path: &str,
current_ref: &ThreadRef,
) -> Result<(Arc<dyn INode>, Vec<u8>, Option<ElfHeader>)> {
let inode = current_ref
) -> Result<(FileRef, Vec<u8>, Option<ElfHeader>)> {
let file_ref = current_ref
.fs()
.read()
.unwrap()
.lookup_inode(file_path)
.map_err(|e| errno!(e.errno(), "cannot find the file"))?;
.open_file(file_path, 0, FileMode::S_IRUSR)?;
let inode = file_ref.as_inode_file()?.inode();
// Make sure the final file to exec is not a directory
let metadata = inode.metadata()?;
@ -105,12 +105,12 @@ pub fn load_file_hdr_to_vec(
.read_elf64_lazy_as_vec()
.map_err(|e| errno!(e.errno(), "failed to read the file"))?;
let elf_header = ElfFile::parse_elf_hdr(&inode, &mut file_buf);
let elf_header = ElfFile::parse_elf_hdr(&file_ref, &mut file_buf);
if let Ok(elf_header) = elf_header {
Ok((inode, file_buf, Some(elf_header)))
Ok((file_ref, file_buf, Some(elf_header)))
} else {
// this file is not ELF format or there is something wrong when parsing
warn!("parse elf header error = {}", elf_header.err().unwrap());
Ok((inode, file_buf, None))
Ok((file_ref, file_buf, None))
}
}

@ -166,7 +166,7 @@ fn new_process_common(
parent_process: Option<ProcessRef>,
) -> Result<ProcessRef> {
let mut argv = argv.clone().to_vec();
let (is_script, elf_inode, mut elf_buf, elf_header) =
let (is_script, elf_file, mut elf_buf, elf_header) =
load_exec_file_hdr_to_vec(file_path, current_ref)?;
// elf_path might be different from file_path because file_path could lead to a script text file.
@ -182,13 +182,13 @@ fn new_process_common(
file_path.to_string()
};
let exec_elf_hdr = ElfFile::new(&elf_inode, &mut elf_buf, elf_header)
let exec_elf_hdr = ElfFile::new(&elf_file, &mut elf_buf, elf_header)
.cause_err(|e| errno!(e.errno(), "invalid executable"))?;
let ldso_path = exec_elf_hdr
.elf_interpreter()
.ok_or_else(|| errno!(EINVAL, "cannot find the interpreter segment"))?;
trace!("ldso_path = {:?}", ldso_path);
let (ldso_inode, mut ldso_elf_hdr_buf, ldso_elf_header) =
let (ldso_file, mut ldso_elf_hdr_buf, ldso_elf_header) =
load_file_hdr_to_vec(ldso_path, current_ref)
.cause_err(|e| errno!(e.errno(), "cannot load ld.so"))?;
let ldso_elf_header = if ldso_elf_header.is_none() {
@ -196,7 +196,7 @@ fn new_process_common(
} else {
ldso_elf_header.unwrap()
};
let ldso_elf_hdr = ElfFile::new(&ldso_inode, &mut ldso_elf_hdr_buf, ldso_elf_header)
let ldso_elf_hdr = ElfFile::new(&ldso_file, &mut ldso_elf_hdr_buf, ldso_elf_header)
.cause_err(|e| errno!(e.errno(), "invalid ld.so"))?;
let new_process_ref = {

@ -15,7 +15,7 @@ const ELF64_HDR_SIZE: usize = 64;
pub struct ElfFile<'a> {
elf_buf: &'a [u8],
elf_inner: Elf<'a>,
file_inode: &'a Arc<dyn INode>,
file_ref: &'a FileRef,
}
impl<'a> Debug for ElfFile<'a> {
@ -30,7 +30,7 @@ impl<'a> Debug for ElfFile<'a> {
impl<'a> ElfFile<'a> {
pub fn new(
file_inode: &'a Arc<dyn INode>,
file_ref: &'a FileRef,
mut elf_buf: &'a mut [u8],
header: ElfHeader,
) -> Result<ElfFile<'a>> {
@ -64,7 +64,7 @@ impl<'a> ElfFile<'a> {
intepreter_offset,
intepreter_count
);
file_inode.read_at(
file_ref.read_at(
intepreter_offset,
&mut elf_buf[intepreter_offset..intepreter_offset + intepreter_count],
);
@ -87,7 +87,7 @@ impl<'a> ElfFile<'a> {
Ok(ElfFile {
elf_buf,
elf_inner,
file_inode,
file_ref,
})
}
@ -107,11 +107,11 @@ impl<'a> ElfFile<'a> {
self.elf_buf
}
pub fn file_inode(&self) -> &Arc<dyn INode> {
self.file_inode
pub fn file_ref(&self) -> &FileRef {
self.file_ref
}
pub fn parse_elf_hdr(inode: &Arc<dyn INode>, elf_buf: &mut Vec<u8>) -> Result<ElfHeader> {
pub fn parse_elf_hdr(elf_file: &FileRef, elf_buf: &mut Vec<u8>) -> Result<ElfHeader> {
// TODO: Sanity check the number of program headers..
let mut phdr_start = 0;
let mut phdr_end = 0;
@ -130,7 +130,7 @@ impl<'a> ElfFile<'a> {
}
let program_hdr_table_size = elf_hdr.e_phnum * elf_hdr.e_phentsize;
inode.read_at(
elf_file.read_at(
elf_hdr.e_phoff as usize,
&mut elf_buf[hdr_size..hdr_size + (program_hdr_table_size as usize)],
)?;

@ -93,11 +93,10 @@ impl Chunk {
}
pub fn new_single_vma_chunk(vm_range: &VMRange, options: &VMMapOptions) -> Result<Self> {
let writeback_file = options.writeback_file().clone();
let vm_area = VMArea::new(
vm_range.clone(),
*options.perms(),
writeback_file,
options.initializer().backed_file(),
DUMMY_CHUNK_PROCESS_ID,
);
// Initialize the memory of the new range
@ -178,7 +177,7 @@ impl Chunk {
if internal_manager.chunk_manager().free_size() < options.size() {
return_errno!(ENOMEM, "no enough size without trying. try other chunks");
}
internal_manager.chunk_manager().mmap(options)
internal_manager.chunk_manager_mut().mmap(options)
}
pub fn is_single_vma(&self) -> bool {
@ -279,7 +278,11 @@ impl ChunkInternal {
self.process_set.insert(pid);
}
pub fn chunk_manager(&mut self) -> &mut ChunkManager {
pub fn chunk_manager(&self) -> &ChunkManager {
&self.chunk_manager
}
pub fn chunk_manager_mut(&mut self) -> &mut ChunkManager {
&mut self.chunk_manager
}

@ -75,9 +75,10 @@ mod vm_util;
use self::vm_layout::VMLayout;
pub use self::chunk::ChunkRef;
pub use self::chunk::{ChunkRef, ChunkType};
pub use self::process_vm::{MMapFlags, MRemapFlags, MSyncFlags, ProcessVM, ProcessVMBuilder};
pub use self::user_space_vm::USER_SPACE_VM_MANAGER;
pub use self::vm_area::VMArea;
pub use self::vm_perms::VMPerms;
pub use self::vm_range::VMRange;
pub use self::vm_util::{VMInitializer, VMMapOptionsBuilder};

@ -7,7 +7,9 @@ use super::process::elf_file::{ElfFile, ProgramHeaderExt};
use super::user_space_vm::USER_SPACE_VM_MANAGER;
use super::vm_area::VMArea;
use super::vm_perms::VMPerms;
use super::vm_util::{VMInitializer, VMMapAddr, VMMapOptions, VMMapOptionsBuilder, VMRemapOptions};
use super::vm_util::{
FileBacked, VMInitializer, VMMapAddr, VMMapOptions, VMMapOptionsBuilder, VMRemapOptions,
};
use std::collections::HashSet;
use std::sync::atomic::{AtomicUsize, Ordering};
@ -117,7 +119,9 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> {
.size(elf_layout.size())
.align(elf_layout.align())
.perms(VMPerms::ALL) // set it to read | write | exec for simplicity
.initializer(VMInitializer::DoNothing())
.initializer(VMInitializer::ElfSpecific {
elf_file: elf_file.file_ref().clone(),
})
.build()
.map_err(|e| {
&self.handle_error_when_init(&chunks);
@ -244,7 +248,7 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> {
}
// Bytes of file_size length are loaded from the ELF file
elf_file.file_inode().read_at(
elf_file.file_ref().read_at(
file_offset,
&mut elf_proc_buf[mem_start_offset..mem_start_offset + file_size],
);
@ -318,6 +322,14 @@ impl ProcessVM {
&self.mem_chunks
}
pub fn stack_range(&self) -> &VMRange {
&self.stack_range
}
pub fn heap_range(&self) -> &VMRange {
&self.heap_range
}
pub fn add_mem_chunk(&self, chunk: ChunkRef) {
let mut mem_chunks = self.mem_chunks.write().unwrap();
mem_chunks.insert(chunk);
@ -488,28 +500,22 @@ impl ProcessVM {
VMInitializer::DoNothing()
} else {
let file_ref = current!().file(fd)?;
VMInitializer::LoadFromFile {
file: file_ref,
offset: offset,
// Only shared, file-backed memory mappings have write-back files
let need_write_back = if flags.contains(MMapFlags::MAP_SHARED) {
true
} else {
false
};
VMInitializer::FileBacked {
file: FileBacked::new(file_ref, offset, need_write_back),
}
}
};
// Only shared, file-backed memory mappings have write-back files
let writeback_file = if flags.contains(MMapFlags::MAP_SHARED) {
if let VMInitializer::LoadFromFile { file, offset } = &initializer {
Some((file.clone(), *offset))
} else {
None
}
} else {
None
};
let mmap_options = VMMapOptionsBuilder::default()
.size(size)
.addr(addr_option)
.perms(perms)
.initializer(initializer)
.writeback_file(writeback_file)
.build()?;
let mmap_addr = USER_SPACE_VM_MANAGER.mmap(&mmap_options)?;
Ok(mmap_addr)

@ -2,6 +2,7 @@ use std::ops::{Deref, DerefMut};
use super::vm_perms::VMPerms;
use super::vm_range::VMRange;
use super::vm_util::FileBacked;
use super::*;
use intrusive_collections::rbtree::{Link, RBTree};
@ -11,7 +12,7 @@ use intrusive_collections::{intrusive_adapter, KeyAdapter};
pub struct VMArea {
range: VMRange,
perms: VMPerms,
writeback_file: Option<(FileRef, usize)>,
file_backed: Option<FileBacked>,
pid: pid_t,
}
@ -19,13 +20,13 @@ impl VMArea {
pub fn new(
range: VMRange,
perms: VMPerms,
writeback_file: Option<(FileRef, usize)>,
file_backed: Option<FileBacked>,
pid: pid_t,
) -> Self {
Self {
range,
perms,
writeback_file,
file_backed,
pid,
}
}
@ -38,20 +39,25 @@ impl VMArea {
new_perms: VMPerms,
pid: pid_t,
) -> Self {
let new_writeback_file = vma.writeback_file.as_ref().map(|(file, file_offset)| {
let new_file = file.clone();
let new_backed_file = vma.file_backed.as_ref().map(|file| {
let mut new_file = file.clone();
let file_offset = file.offset();
let new_file_offset = if vma.start() < new_range.start() {
let vma_offset = new_range.start() - vma.start();
*file_offset + vma_offset
file_offset + vma_offset
} else {
let vma_offset = vma.start() - new_range.start();
debug_assert!(*file_offset >= vma_offset);
*file_offset - vma_offset
debug_assert!(file_offset >= vma_offset);
file_offset - vma_offset
};
(new_file, new_file_offset)
new_file.set_offset(new_file_offset);
new_file
});
Self::new(new_range, new_perms, new_writeback_file, pid)
Self::new(new_range, new_perms, new_backed_file, pid)
}
pub fn perms(&self) -> VMPerms {
@ -66,8 +72,20 @@ impl VMArea {
self.pid
}
pub fn writeback_file(&self) -> &Option<(FileRef, usize)> {
&self.writeback_file
pub fn init_file(&self) -> Option<(&FileRef, usize)> {
if let Some(file) = &self.file_backed {
Some(file.init_file())
} else {
None
}
}
pub fn writeback_file(&self) -> Option<(&FileRef, usize)> {
if let Some(file) = &self.file_backed {
file.writeback_file()
} else {
None
}
}
pub fn set_perms(&mut self, new_perms: VMPerms) {
@ -103,15 +121,19 @@ impl VMArea {
let old_start = self.start();
self.range.set_start(new_start);
// If the updates to the VMA needs to write back to a file, then the
// file offset must be adjusted according to the new start address.
if let Some((_, offset)) = self.writeback_file.as_mut() {
if let Some(file) = self.file_backed.as_mut() {
if !file.need_write_back() {
return;
}
// If the updates to the VMA needs to write back to a file, then the
// file offset must be adjusted according to the new start address.
let offset = file.offset();
if old_start < new_start {
*offset += new_start - old_start;
file.set_offset(offset + (new_start - old_start));
} else {
// The caller must guarantee that the new start makes sense
debug_assert!(*offset >= old_start - new_start);
*offset -= old_start - new_start;
debug_assert!(offset >= old_start - new_start);
file.set_offset(offset - (old_start - new_start));
}
}
}
@ -223,22 +245,3 @@ impl VMAObj {
&self.vma
}
}
impl VMArea {
pub fn new_obj(
range: VMRange,
perms: VMPerms,
writeback_file: Option<(FileRef, usize)>,
pid: pid_t,
) -> Box<VMAObj> {
Box::new(VMAObj {
link: Link::new(),
vma: VMArea {
range,
perms,
writeback_file,
pid,
},
})
}
}

@ -116,9 +116,13 @@ impl ChunkManager {
.free_manager
.find_free_range_internal(size, align, addr)?;
let new_addr = new_range.start();
let writeback_file = options.writeback_file().clone();
let current_pid = current!().process().pid();
let new_vma = VMArea::new(new_range, *options.perms(), writeback_file, current_pid);
let new_vma = VMArea::new(
new_range,
*options.perms(),
options.initializer().backed_file(),
current_pid,
);
// Initialize the memory of the new range
let buf = unsafe { new_vma.as_slice_mut() };
@ -416,7 +420,7 @@ impl ChunkManager {
/// Same as flush_vma, except that an extra condition on the file needs to satisfy.
pub fn flush_file_vma_with_cond<F: Fn(&FileRef) -> bool>(vma: &VMArea, cond_fn: F) {
let (file, file_offset) = match vma.writeback_file().as_ref() {
let (file, file_offset) = match vma.writeback_file() {
None => return,
Some((file_and_offset)) => file_and_offset,
};
@ -430,7 +434,7 @@ impl ChunkManager {
if !cond_fn(file) {
return;
}
file.write_at(*file_offset, unsafe { vma.as_slice() });
file.write_at(file_offset, unsafe { vma.as_slice() });
}
pub fn find_mmap_region(&self, addr: usize) -> Result<VMRange> {

@ -184,7 +184,11 @@ impl VMManager {
"mmap with addr in existing default chunk: {:?}",
chunk.range()
);
return chunk_internal.lock().unwrap().chunk_manager().mmap(options);
return chunk_internal
.lock()
.unwrap()
.chunk_manager_mut()
.mmap(options);
}
ChunkType::SingleVMA(_) => {
match addr {
@ -287,7 +291,7 @@ impl VMManager {
ChunkType::MultiVMA(manager) => manager
.lock()
.unwrap()
.chunk_manager()
.chunk_manager_mut()
.munmap_range(munmap_range)?,
}
}
@ -307,7 +311,7 @@ impl VMManager {
return manager
.lock()
.unwrap()
.chunk_manager()
.chunk_manager_mut()
.munmap_range(munmap_range);
}
ChunkType::SingleVMA(_) => {
@ -353,7 +357,7 @@ impl VMManager {
return manager
.lock()
.unwrap()
.chunk_manager()
.chunk_manager_mut()
.mprotect(addr, size, perms);
}
ChunkType::SingleVMA(_) => {
@ -383,7 +387,7 @@ impl VMManager {
return manager
.lock()
.unwrap()
.chunk_manager()
.chunk_manager_mut()
.msync_by_range(&sync_range);
}
ChunkType::SingleVMA(vma) => {
@ -405,7 +409,7 @@ impl VMManager {
manager
.lock()
.unwrap()
.chunk_manager()
.chunk_manager_mut()
.msync_by_file(sync_file);
}
ChunkType::SingleVMA(vma) => {
@ -452,7 +456,7 @@ impl VMManager {
ChunkType::MultiVMA(manager) => manager
.lock()
.unwrap()
.chunk_manager()
.chunk_manager_mut()
.parse_mremap_options(options),
ChunkType::SingleVMA(vma) => {
self.parse_mremap_options_for_single_vma_chunk(options, vma)

@ -55,6 +55,26 @@ impl VMPerms {
assert!(sgx_status == sgx_status_t::SGX_SUCCESS && retval == 0);
}
}
pub fn display(&self) -> String {
let mut str = String::new();
if self.can_read() {
str += "r";
} else {
str += "-";
}
if self.can_write() {
str += "w";
} else {
str += "-";
}
if self.can_execute() {
str += "x";
} else {
str += "-";
}
str
}
}
impl Default for VMPerms {

@ -1,9 +1,8 @@
use super::*;
// use super::vm_area::VMArea;
// use super::free_space_manager::VMFreeSpaceManager;
use super::vm_area::*;
use super::vm_perms::VMPerms;
use crate::fs::FileMode;
use std::collections::BTreeSet;
use intrusive_collections::rbtree::{Link, RBTree};
@ -18,15 +17,19 @@ pub enum VMInitializer {
CopyFrom {
range: VMRange,
},
LoadFromFile {
file: FileRef,
offset: usize,
FileBacked {
file: FileBacked,
},
// For ELF files, there is specical handling to not copy all the contents of the file. This is only used for tracking.
ElfSpecific {
elf_file: FileRef,
},
// For file-backed mremap which may move from old range to new range and read extra bytes from file
CopyOldAndReadNew {
old_range: VMRange,
file: FileRef,
offset: usize, // read file from this offset
new_writeback_file: FileBacked,
},
}
@ -39,7 +42,7 @@ impl Default for VMInitializer {
impl VMInitializer {
pub fn init_slice(&self, buf: &mut [u8]) -> Result<()> {
match self {
VMInitializer::DoNothing() => {
VMInitializer::DoNothing() | VMInitializer::ElfSpecific { .. } => {
// Do nothing
}
VMInitializer::FillZeros() => {
@ -55,10 +58,11 @@ impl VMInitializer {
*b = 0;
}
}
VMInitializer::LoadFromFile { file, offset } => {
VMInitializer::FileBacked { file } => {
// TODO: make sure that read_at does not move file cursor
let len = file
.read_at(*offset, buf)
.file_ref()
.read_at(file.offset(), buf)
.cause_err(|_| errno!(EACCES, "failed to init memory from file"))?;
for b in &mut buf[len..] {
*b = 0;
@ -68,6 +72,7 @@ impl VMInitializer {
old_range,
file,
offset,
new_writeback_file,
} => {
// TODO: Handle old_range with non-readable subrange
let src_slice = unsafe { old_range.as_slice() };
@ -85,6 +90,66 @@ impl VMInitializer {
}
Ok(())
}
pub fn backed_file(&self) -> Option<FileBacked> {
match self {
VMInitializer::ElfSpecific { elf_file } => {
let file_ref = elf_file.clone();
Some(FileBacked::new(file_ref, 0, false))
}
VMInitializer::FileBacked { file } => Some(file.clone()),
VMInitializer::CopyOldAndReadNew {
new_writeback_file, ..
} => Some(new_writeback_file.clone()),
_ => None,
}
}
}
// This struct is used to record file-backed memory type.
#[derive(Debug, Clone)]
pub struct FileBacked {
file: FileRef,
offset: usize,
write_back: bool,
}
impl FileBacked {
pub fn new(file: FileRef, offset: usize, write_back: bool) -> Self {
Self {
file,
offset,
write_back,
}
}
pub fn file_ref(&self) -> &FileRef {
&self.file
}
pub fn offset(&self) -> usize {
self.offset
}
pub fn set_offset(&mut self, offset: usize) {
self.offset = offset;
}
pub fn need_write_back(&self) -> bool {
self.write_back
}
pub fn init_file(&self) -> (&FileRef, usize) {
(&self.file, self.offset)
}
pub fn writeback_file(&self) -> Option<(&FileRef, usize)> {
if self.write_back {
Some((&self.file, self.offset))
} else {
None
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
@ -118,8 +183,6 @@ pub struct VMMapOptions {
perms: VMPerms,
addr: VMMapAddr,
initializer: VMInitializer,
// The content of the VMA can be written back to a given file at a given offset
writeback_file: Option<(FileRef, usize)>,
}
// VMMapOptionsBuilder is generated automatically, except the build function
@ -165,14 +228,12 @@ impl VMMapOptionsBuilder {
Some(initializer) => initializer.clone(),
None => VMInitializer::default(),
};
let writeback_file = self.writeback_file.take().unwrap_or_default();
Ok(VMMapOptions {
size,
align,
perms,
addr,
initializer,
writeback_file,
})
}
}
@ -197,10 +258,6 @@ impl VMMapOptions {
pub fn initializer(&self) -> &VMInitializer {
&self.initializer
}
pub fn writeback_file(&self) -> &Option<(FileRef, usize)> {
&self.writeback_file
}
}
#[derive(Clone, Copy, PartialEq)]
@ -375,17 +432,18 @@ pub trait VMRemapParser {
}
(MRemapFlags::None, VMRemapSizeType::Growing, Some((backed_file, offset))) => {
// Update writeback file offset
let new_writeback_file = Some((backed_file.clone(), offset + vma.size()));
let vm_initializer_for_new_range = VMInitializer::LoadFromFile {
file: backed_file.clone(),
offset: offset + vma.size(), // file-backed mremap should start from the end of previous mmap/mremap file
let vm_initializer_for_new_range = VMInitializer::FileBacked {
file: FileBacked::new(
backed_file.clone(),
offset + vma.size(), // file-backed mremap should start from the end of previous mmap/mremap file
true,
),
};
let mmap_opts = VMMapOptionsBuilder::default()
.size(new_size - old_size)
.addr(VMMapAddr::Need(old_range.end()))
.perms(perms)
.initializer(vm_initializer_for_new_range)
.writeback_file(new_writeback_file)
.build()?;
let ret_addr = Some(old_addr);
(Some(mmap_opts), ret_addr)
@ -421,18 +479,18 @@ pub trait VMRemapParser {
VMRange::new_with_size(old_addr + old_size, new_size - old_size)?;
if self.is_free_range(&prefered_new_range) {
// Don't need to move the old range
let vm_initializer_for_new_range = VMInitializer::LoadFromFile {
file: backed_file.clone(),
offset: offset + vma.size(), // file-backed mremap should start from the end of previous mmap/mremap file
let vm_initializer_for_new_range = VMInitializer::FileBacked {
file: FileBacked::new(
backed_file.clone(),
offset + vma.size(), // file-backed mremap should start from the end of previous mmap/mremap file
true,
),
};
// Write back file should start from new offset
let new_writeback_file = Some((backed_file.clone(), offset + vma.size()));
let mmap_ops = VMMapOptionsBuilder::default()
.size(prefered_new_range.size())
.addr(VMMapAddr::Need(prefered_new_range.start()))
.perms(perms)
.initializer(vm_initializer_for_new_range)
.writeback_file(new_writeback_file)
.build()?;
(Some(mmap_ops), Some(old_addr))
} else {
@ -441,19 +499,19 @@ pub trait VMRemapParser {
let copy_end = vma.end();
let copy_range = VMRange::new(old_range.start(), copy_end)?;
let reread_file_start_offset = copy_end - vma.start();
let new_writeback_file = FileBacked::new(backed_file.clone(), offset, true);
VMInitializer::CopyOldAndReadNew {
old_range: copy_range,
file: backed_file.clone(),
offset: reread_file_start_offset,
new_writeback_file: new_writeback_file,
}
};
let new_writeback_file = Some((backed_file.clone(), *offset));
let mmap_ops = VMMapOptionsBuilder::default()
.size(new_size)
.addr(VMMapAddr::Any)
.perms(perms)
.initializer(vm_initializer_for_new_range)
.writeback_file(new_writeback_file)
.build()?;
// Cannot determine the returned address for now, which can only be obtained after calling mmap
let ret_addr = None;
@ -476,19 +534,19 @@ pub trait VMRemapParser {
let copy_end = vma.end();
let copy_range = VMRange::new(old_range.start(), copy_end)?;
let reread_file_start_offset = copy_end - vma.start();
let new_writeback_file = FileBacked::new(backed_file.clone(), offset, true);
VMInitializer::CopyOldAndReadNew {
old_range: copy_range,
file: backed_file.clone(),
offset: reread_file_start_offset,
new_writeback_file: new_writeback_file,
}
};
let new_writeback_file = Some((backed_file.clone(), *offset));
let mmap_opts = VMMapOptionsBuilder::default()
.size(new_size)
.addr(VMMapAddr::Force(new_addr))
.perms(perms)
.initializer(vm_initializer_for_new_range)
.writeback_file(new_writeback_file)
.build()?;
let ret_addr = Some(new_addr);
(Some(mmap_opts), ret_addr)

@ -44,6 +44,7 @@ static int test_read_from_procfs(const char *proc_inode) {
}
} while (len == sizeof(buf));
close(fd);
printf("test read from %s:\n%s\n", proc_inode, buf);
return 0;
}
@ -89,6 +90,15 @@ static int test_readlink_from_proc_self_cwd() {
return 0;
}
static int test_read_from_proc_self_maps() {
const char *proc_maps = "/proc/self/maps";
if (test_read_from_procfs(proc_maps) < 0) {
THROW_ERROR("failed to read the cpuinfo");
}
return 0;
}
static int test_readlink_from_proc_self_root() {
char root_buf[PATH_MAX] = { 0 };
const char *proc_root = "/proc/self/root";
@ -282,6 +292,7 @@ static test_case_t test_cases[] = {
TEST_CASE(test_readdir_root),
TEST_CASE(test_readdir_self),
TEST_CASE(test_readdir_self_fd),
TEST_CASE(test_read_from_proc_self_maps),
};
int main(int argc, const char *argv[]) {