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 { 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> { pub fn open(inode: Arc<dyn INode>, abs_path: &str, flags: u32) -> Result<Self> {
let access_mode = AccessMode::from_u32(flags)?; let access_mode = AccessMode::from_u32(flags)?;
if (access_mode.readable() && !inode.allow_read()?) { 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::cwd::ProcCwdSymINode;
use self::exe::ProcExeSymINode; use self::exe::ProcExeSymINode;
use self::fd::LockedProcFdDirINode; use self::fd::LockedProcFdDirINode;
use self::maps::ProcMapsINode;
use self::root::ProcRootSymINode; use self::root::ProcRootSymINode;
use self::stat::ProcStatINode; use self::stat::ProcStatINode;
@ -15,6 +16,7 @@ mod comm;
mod cwd; mod cwd;
mod exe; mod exe;
mod fd; mod fd;
mod maps;
mod root; mod root;
mod stat; mod stat;
@ -60,6 +62,9 @@ impl LockedPidDirINode {
// stat // stat
let stat_inode = ProcStatINode::new(&file.process_ref); let stat_inode = ProcStatINode::new(&file.process_ref);
file.entries.insert(String::from("stat"), stat_inode); 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(()) Ok(())
} }

@ -1,6 +1,6 @@
use super::super::elf_file::*; use super::super::elf_file::*;
use super::ThreadRef; use super::ThreadRef;
use crate::fs::{FileMode, INodeExt}; use crate::fs::{AsINodeFile, FileMode, INodeExt};
use crate::prelude::*; use crate::prelude::*;
use rcore_fs::vfs::{FileType, INode, Metadata}; use rcore_fs::vfs::{FileType, INode, Metadata};
use std::ffi::CString; use std::ffi::CString;
@ -13,10 +13,10 @@ use std::ffi::CString;
pub fn load_exec_file_hdr_to_vec( pub fn load_exec_file_hdr_to_vec(
file_path: &str, file_path: &str,
current_ref: &ThreadRef, current_ref: &ThreadRef,
) -> Result<(Option<String>, Arc<dyn INode>, Vec<u8>, ElfHeader)> { ) -> Result<(Option<String>, FileRef, Vec<u8>, ElfHeader)> {
let (inode, file_buf, elf_hdr) = load_file_hdr_to_vec(&file_path, current_ref)?; let (file_ref, file_buf, elf_hdr) = load_file_hdr_to_vec(&file_path, current_ref)?;
if elf_hdr.is_some() { if elf_hdr.is_some() {
Ok((None, inode, file_buf, elf_hdr.unwrap())) Ok((None, file_ref, file_buf, elf_hdr.unwrap()))
} else { } else {
// loaded file is not Elf format, try script file // loaded file is not Elf format, try script file
if !is_script_file(&file_buf) { 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" "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)?; load_file_hdr_to_vec(&interpreter_path, current_ref)?;
let interp_hdr = if interp_hdr.is_none() { let interp_hdr = if interp_hdr.is_none() {
return_errno!(ENOEXEC, "scrip interpreter is not ELF format"); return_errno!(ENOEXEC, "scrip interpreter is not ELF format");
} else { } else {
interp_hdr.unwrap() 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( pub fn load_file_hdr_to_vec(
file_path: &str, file_path: &str,
current_ref: &ThreadRef, current_ref: &ThreadRef,
) -> Result<(Arc<dyn INode>, Vec<u8>, Option<ElfHeader>)> { ) -> Result<(FileRef, Vec<u8>, Option<ElfHeader>)> {
let inode = current_ref let file_ref = current_ref
.fs() .fs()
.read() .read()
.unwrap() .unwrap()
.lookup_inode(file_path) .open_file(file_path, 0, FileMode::S_IRUSR)?;
.map_err(|e| errno!(e.errno(), "cannot find the file"))?; let inode = file_ref.as_inode_file()?.inode();
// Make sure the final file to exec is not a directory // Make sure the final file to exec is not a directory
let metadata = inode.metadata()?; let metadata = inode.metadata()?;
@ -105,12 +105,12 @@ pub fn load_file_hdr_to_vec(
.read_elf64_lazy_as_vec() .read_elf64_lazy_as_vec()
.map_err(|e| errno!(e.errno(), "failed to read the file"))?; .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 { if let Ok(elf_header) = elf_header {
Ok((inode, file_buf, Some(elf_header))) Ok((file_ref, file_buf, Some(elf_header)))
} else { } else {
// this file is not ELF format or there is something wrong when parsing // this file is not ELF format or there is something wrong when parsing
warn!("parse elf header error = {}", elf_header.err().unwrap()); 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>, parent_process: Option<ProcessRef>,
) -> Result<ProcessRef> { ) -> Result<ProcessRef> {
let mut argv = argv.clone().to_vec(); 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)?; 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. // 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() 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"))?; .cause_err(|e| errno!(e.errno(), "invalid executable"))?;
let ldso_path = exec_elf_hdr let ldso_path = exec_elf_hdr
.elf_interpreter() .elf_interpreter()
.ok_or_else(|| errno!(EINVAL, "cannot find the interpreter segment"))?; .ok_or_else(|| errno!(EINVAL, "cannot find the interpreter segment"))?;
trace!("ldso_path = {:?}", ldso_path); 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) load_file_hdr_to_vec(ldso_path, current_ref)
.cause_err(|e| errno!(e.errno(), "cannot load ld.so"))?; .cause_err(|e| errno!(e.errno(), "cannot load ld.so"))?;
let ldso_elf_header = if ldso_elf_header.is_none() { let ldso_elf_header = if ldso_elf_header.is_none() {
@ -196,7 +196,7 @@ fn new_process_common(
} else { } else {
ldso_elf_header.unwrap() 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"))?; .cause_err(|e| errno!(e.errno(), "invalid ld.so"))?;
let new_process_ref = { let new_process_ref = {

@ -15,7 +15,7 @@ const ELF64_HDR_SIZE: usize = 64;
pub struct ElfFile<'a> { pub struct ElfFile<'a> {
elf_buf: &'a [u8], elf_buf: &'a [u8],
elf_inner: Elf<'a>, elf_inner: Elf<'a>,
file_inode: &'a Arc<dyn INode>, file_ref: &'a FileRef,
} }
impl<'a> Debug for ElfFile<'a> { impl<'a> Debug for ElfFile<'a> {
@ -30,7 +30,7 @@ impl<'a> Debug for ElfFile<'a> {
impl<'a> ElfFile<'a> { impl<'a> ElfFile<'a> {
pub fn new( pub fn new(
file_inode: &'a Arc<dyn INode>, file_ref: &'a FileRef,
mut elf_buf: &'a mut [u8], mut elf_buf: &'a mut [u8],
header: ElfHeader, header: ElfHeader,
) -> Result<ElfFile<'a>> { ) -> Result<ElfFile<'a>> {
@ -64,7 +64,7 @@ impl<'a> ElfFile<'a> {
intepreter_offset, intepreter_offset,
intepreter_count intepreter_count
); );
file_inode.read_at( file_ref.read_at(
intepreter_offset, intepreter_offset,
&mut elf_buf[intepreter_offset..intepreter_offset + intepreter_count], &mut elf_buf[intepreter_offset..intepreter_offset + intepreter_count],
); );
@ -87,7 +87,7 @@ impl<'a> ElfFile<'a> {
Ok(ElfFile { Ok(ElfFile {
elf_buf, elf_buf,
elf_inner, elf_inner,
file_inode, file_ref,
}) })
} }
@ -107,11 +107,11 @@ impl<'a> ElfFile<'a> {
self.elf_buf self.elf_buf
} }
pub fn file_inode(&self) -> &Arc<dyn INode> { pub fn file_ref(&self) -> &FileRef {
self.file_inode 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.. // TODO: Sanity check the number of program headers..
let mut phdr_start = 0; let mut phdr_start = 0;
let mut phdr_end = 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; 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, elf_hdr.e_phoff as usize,
&mut elf_buf[hdr_size..hdr_size + (program_hdr_table_size 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> { 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( let vm_area = VMArea::new(
vm_range.clone(), vm_range.clone(),
*options.perms(), *options.perms(),
writeback_file, options.initializer().backed_file(),
DUMMY_CHUNK_PROCESS_ID, DUMMY_CHUNK_PROCESS_ID,
); );
// Initialize the memory of the new range // Initialize the memory of the new range
@ -178,7 +177,7 @@ impl Chunk {
if internal_manager.chunk_manager().free_size() < options.size() { if internal_manager.chunk_manager().free_size() < options.size() {
return_errno!(ENOMEM, "no enough size without trying. try other chunks"); 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 { pub fn is_single_vma(&self) -> bool {
@ -279,7 +278,11 @@ impl ChunkInternal {
self.process_set.insert(pid); 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 &mut self.chunk_manager
} }

@ -75,9 +75,10 @@ mod vm_util;
use self::vm_layout::VMLayout; 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::process_vm::{MMapFlags, MRemapFlags, MSyncFlags, ProcessVM, ProcessVMBuilder};
pub use self::user_space_vm::USER_SPACE_VM_MANAGER; 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_perms::VMPerms;
pub use self::vm_range::VMRange; pub use self::vm_range::VMRange;
pub use self::vm_util::{VMInitializer, VMMapOptionsBuilder}; 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::user_space_vm::USER_SPACE_VM_MANAGER;
use super::vm_area::VMArea; use super::vm_area::VMArea;
use super::vm_perms::VMPerms; 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::collections::HashSet;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
@ -117,7 +119,9 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> {
.size(elf_layout.size()) .size(elf_layout.size())
.align(elf_layout.align()) .align(elf_layout.align())
.perms(VMPerms::ALL) // set it to read | write | exec for simplicity .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() .build()
.map_err(|e| { .map_err(|e| {
&self.handle_error_when_init(&chunks); &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 // 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, file_offset,
&mut elf_proc_buf[mem_start_offset..mem_start_offset + file_size], &mut elf_proc_buf[mem_start_offset..mem_start_offset + file_size],
); );
@ -318,6 +322,14 @@ impl ProcessVM {
&self.mem_chunks &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) { pub fn add_mem_chunk(&self, chunk: ChunkRef) {
let mut mem_chunks = self.mem_chunks.write().unwrap(); let mut mem_chunks = self.mem_chunks.write().unwrap();
mem_chunks.insert(chunk); mem_chunks.insert(chunk);
@ -488,28 +500,22 @@ impl ProcessVM {
VMInitializer::DoNothing() VMInitializer::DoNothing()
} else { } else {
let file_ref = current!().file(fd)?; let file_ref = current!().file(fd)?;
VMInitializer::LoadFromFile {
file: file_ref,
offset: offset,
}
}
};
// Only shared, file-backed memory mappings have write-back files // Only shared, file-backed memory mappings have write-back files
let writeback_file = if flags.contains(MMapFlags::MAP_SHARED) { let need_write_back = if flags.contains(MMapFlags::MAP_SHARED) {
if let VMInitializer::LoadFromFile { file, offset } = &initializer { true
Some((file.clone(), *offset))
} else { } else {
None false
};
VMInitializer::FileBacked {
file: FileBacked::new(file_ref, offset, need_write_back),
}
} }
} else {
None
}; };
let mmap_options = VMMapOptionsBuilder::default() let mmap_options = VMMapOptionsBuilder::default()
.size(size) .size(size)
.addr(addr_option) .addr(addr_option)
.perms(perms) .perms(perms)
.initializer(initializer) .initializer(initializer)
.writeback_file(writeback_file)
.build()?; .build()?;
let mmap_addr = USER_SPACE_VM_MANAGER.mmap(&mmap_options)?; let mmap_addr = USER_SPACE_VM_MANAGER.mmap(&mmap_options)?;
Ok(mmap_addr) Ok(mmap_addr)

@ -2,6 +2,7 @@ use std::ops::{Deref, DerefMut};
use super::vm_perms::VMPerms; use super::vm_perms::VMPerms;
use super::vm_range::VMRange; use super::vm_range::VMRange;
use super::vm_util::FileBacked;
use super::*; use super::*;
use intrusive_collections::rbtree::{Link, RBTree}; use intrusive_collections::rbtree::{Link, RBTree};
@ -11,7 +12,7 @@ use intrusive_collections::{intrusive_adapter, KeyAdapter};
pub struct VMArea { pub struct VMArea {
range: VMRange, range: VMRange,
perms: VMPerms, perms: VMPerms,
writeback_file: Option<(FileRef, usize)>, file_backed: Option<FileBacked>,
pid: pid_t, pid: pid_t,
} }
@ -19,13 +20,13 @@ impl VMArea {
pub fn new( pub fn new(
range: VMRange, range: VMRange,
perms: VMPerms, perms: VMPerms,
writeback_file: Option<(FileRef, usize)>, file_backed: Option<FileBacked>,
pid: pid_t, pid: pid_t,
) -> Self { ) -> Self {
Self { Self {
range, range,
perms, perms,
writeback_file, file_backed,
pid, pid,
} }
} }
@ -38,20 +39,25 @@ impl VMArea {
new_perms: VMPerms, new_perms: VMPerms,
pid: pid_t, pid: pid_t,
) -> Self { ) -> Self {
let new_writeback_file = vma.writeback_file.as_ref().map(|(file, file_offset)| { let new_backed_file = vma.file_backed.as_ref().map(|file| {
let new_file = file.clone(); let mut new_file = file.clone();
let file_offset = file.offset();
let new_file_offset = if vma.start() < new_range.start() { let new_file_offset = if vma.start() < new_range.start() {
let vma_offset = new_range.start() - vma.start(); let vma_offset = new_range.start() - vma.start();
*file_offset + vma_offset file_offset + vma_offset
} else { } else {
let vma_offset = vma.start() - new_range.start(); let vma_offset = vma.start() - new_range.start();
debug_assert!(*file_offset >= vma_offset); debug_assert!(file_offset >= vma_offset);
*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 { pub fn perms(&self) -> VMPerms {
@ -66,8 +72,20 @@ impl VMArea {
self.pid self.pid
} }
pub fn writeback_file(&self) -> &Option<(FileRef, usize)> { pub fn init_file(&self) -> Option<(&FileRef, usize)> {
&self.writeback_file 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) { pub fn set_perms(&mut self, new_perms: VMPerms) {
@ -103,15 +121,19 @@ impl VMArea {
let old_start = self.start(); let old_start = self.start();
self.range.set_start(new_start); self.range.set_start(new_start);
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 // 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. // file offset must be adjusted according to the new start address.
if let Some((_, offset)) = self.writeback_file.as_mut() { let offset = file.offset();
if old_start < new_start { if old_start < new_start {
*offset += new_start - old_start; file.set_offset(offset + (new_start - old_start));
} else { } else {
// The caller must guarantee that the new start makes sense // The caller must guarantee that the new start makes sense
debug_assert!(*offset >= old_start - new_start); debug_assert!(offset >= old_start - new_start);
*offset -= old_start - new_start; file.set_offset(offset - (old_start - new_start));
} }
} }
} }
@ -223,22 +245,3 @@ impl VMAObj {
&self.vma &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 .free_manager
.find_free_range_internal(size, align, addr)?; .find_free_range_internal(size, align, addr)?;
let new_addr = new_range.start(); let new_addr = new_range.start();
let writeback_file = options.writeback_file().clone();
let current_pid = current!().process().pid(); 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 // Initialize the memory of the new range
let buf = unsafe { new_vma.as_slice_mut() }; 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. /// 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) { 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, None => return,
Some((file_and_offset)) => file_and_offset, Some((file_and_offset)) => file_and_offset,
}; };
@ -430,7 +434,7 @@ impl ChunkManager {
if !cond_fn(file) { if !cond_fn(file) {
return; 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> { pub fn find_mmap_region(&self, addr: usize) -> Result<VMRange> {

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

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

@ -44,6 +44,7 @@ static int test_read_from_procfs(const char *proc_inode) {
} }
} while (len == sizeof(buf)); } while (len == sizeof(buf));
close(fd); close(fd);
printf("test read from %s:\n%s\n", proc_inode, buf);
return 0; return 0;
} }
@ -89,6 +90,15 @@ static int test_readlink_from_proc_self_cwd() {
return 0; 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() { static int test_readlink_from_proc_self_root() {
char root_buf[PATH_MAX] = { 0 }; char root_buf[PATH_MAX] = { 0 };
const char *proc_root = "/proc/self/root"; 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_root),
TEST_CASE(test_readdir_self), TEST_CASE(test_readdir_self),
TEST_CASE(test_readdir_self_fd), TEST_CASE(test_readdir_self_fd),
TEST_CASE(test_read_from_proc_self_maps),
}; };
int main(int argc, const char *argv[]) { int main(int argc, const char *argv[]) {