Refactor the definition of page properties and permissions

This commit is contained in:
Zhang Junyang
2024-05-05 20:51:38 +08:00
committed by Tate, Hongliang Tian
parent 351e08c897
commit 989970429a
24 changed files with 538 additions and 664 deletions

View File

@ -4,10 +4,7 @@
//! When create a process from elf file, we will use the elf_load_info to construct the VmSpace
use align_ext::AlignExt;
use aster_frame::{
task::Task,
vm::{VmIo, VmPerm},
};
use aster_frame::{task::Task, vm::VmIo};
use aster_rights::{Full, Rights};
use xmas_elf::program::{self, ProgramHeader64};
@ -262,7 +259,7 @@ fn map_segment_vmo(
root_vmar: &Vmar<Full>,
base_addr: Vaddr,
) -> Result<()> {
let perms = VmPerms::from(parse_segment_perm(program_header.flags));
let perms = parse_segment_perm(program_header.flags);
let offset = (program_header.virtual_addr as Vaddr).align_down(PAGE_SIZE);
trace!(
"map segment vmo: virtual addr = 0x{:x}, size = 0x{:x}, perms = {:?}",
@ -359,16 +356,16 @@ fn init_segment_vmo(program_header: &ProgramHeader64, elf_file: &Dentry) -> Resu
Ok((segment_vmo.to_dyn(), anonymous_map_size))
}
fn parse_segment_perm(flags: xmas_elf::program::Flags) -> VmPerm {
let mut vm_perm = VmPerm::empty();
fn parse_segment_perm(flags: xmas_elf::program::Flags) -> VmPerms {
let mut vm_perm = VmPerms::empty();
if flags.is_read() {
vm_perm |= VmPerm::R;
vm_perm |= VmPerms::READ;
}
if flags.is_write() {
vm_perm |= VmPerm::W;
vm_perm |= VmPerms::WRITE;
}
if flags.is_execute() {
vm_perm |= VmPerm::X;
vm_perm |= VmPerms::EXEC;
}
vm_perm
}

View File

@ -3,7 +3,6 @@
//! This mod defines mmap flags and the handler to syscall mmap
use align_ext::AlignExt;
use aster_frame::vm::VmPerm;
use aster_rights::Rights;
use super::SyscallReturn;
@ -27,7 +26,7 @@ pub fn sys_mmap(
offset: u64,
) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_MMAP);
let perms = VmPerm::try_from(perms).unwrap();
let perms = VmPerms::from_posix_prot_bits(perms as u32).unwrap();
let option = MMapOptions::try_from(flags as u32)?;
let res = do_sys_mmap(
addr as usize,
@ -43,14 +42,14 @@ pub fn sys_mmap(
fn do_sys_mmap(
addr: Vaddr,
len: usize,
vm_perm: VmPerm,
vm_perms: VmPerms,
option: MMapOptions,
fd: FileDesc,
offset: usize,
) -> Result<Vaddr> {
debug!(
"addr = 0x{:x}, len = 0x{:x}, perms = {:?}, option = {:?}, fd = {}, offset = 0x{:x}",
addr, len, vm_perm, option, fd, offset
addr, len, vm_perms, option, fd, offset
);
let len = len.align_up(PAGE_SIZE);
@ -58,7 +57,6 @@ fn do_sys_mmap(
if offset % PAGE_SIZE != 0 {
return_errno_with_message!(Errno::EINVAL, "mmap only support page-aligned offset");
}
let perms = VmPerms::from(vm_perm);
let vmo = if option.flags.contains(MMapFlags::MAP_ANONYMOUS) {
if offset != 0 {
@ -72,7 +70,7 @@ fn do_sys_mmap(
let current = current!();
let root_vmar = current.root_vmar();
let vm_map_options = {
let mut options = root_vmar.new_map(vmo.to_dyn(), perms)?;
let mut options = root_vmar.new_map(vmo.to_dyn(), vm_perms)?;
let flags = option.flags;
if flags.contains(MMapFlags::MAP_FIXED) {
options = options.offset(addr).can_overwrite(true);

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use aster_frame::vm::VmPerm;
use aster_frame::vm::PageFlags;
use aster_rights::Rights;
use bitflags::bitflags;
@ -16,6 +16,12 @@ bitflags! {
}
}
impl VmPerms {
pub fn from_posix_prot_bits(bits: u32) -> Option<Self> {
VmPerms::from_bits(bits)
}
}
impl From<Rights> for VmPerms {
fn from(rights: Rights) -> VmPerms {
let mut vm_perm = VmPerms::empty();
@ -48,34 +54,34 @@ impl From<VmPerms> for Rights {
}
}
impl From<VmPerm> for VmPerms {
fn from(perm: VmPerm) -> Self {
impl From<PageFlags> for VmPerms {
fn from(flags: PageFlags) -> Self {
let mut perms = VmPerms::empty();
if perm.contains(VmPerm::R) {
if flags.contains(PageFlags::R) {
perms |= VmPerms::READ;
}
if perm.contains(VmPerm::W) {
if flags.contains(PageFlags::W) {
perms |= VmPerms::WRITE;
}
if perm.contains(VmPerm::X) {
if flags.contains(PageFlags::X) {
perms |= VmPerms::EXEC;
}
perms
}
}
impl From<VmPerms> for VmPerm {
fn from(perms: VmPerms) -> Self {
let mut perm = VmPerm::U;
if perms.contains(VmPerms::READ) {
perm |= VmPerm::R;
impl From<VmPerms> for PageFlags {
fn from(val: VmPerms) -> Self {
let mut flags = PageFlags::empty();
if val.contains(VmPerms::READ) {
flags |= PageFlags::R;
}
if perms.contains(VmPerms::WRITE) {
perm |= VmPerm::W;
if val.contains(VmPerms::WRITE) {
flags |= PageFlags::W;
}
if perms.contains(VmPerms::EXEC) {
perm |= VmPerm::X;
if val.contains(VmPerms::EXEC) {
flags |= PageFlags::X;
}
perm
flags
}
}

View File

@ -48,7 +48,7 @@ impl Vmar<Rights> {
///
/// This method requires the following access rights:
/// 1. The VMAR contains the rights corresponding to the memory permissions of
/// the mapping. For example, if `perms` contains `VmPerm::WRITE`,
/// the mapping. For example, if `perms` contains `VmPerms::WRITE`,
/// then the VMAR must have the Write right.
/// 2. Similarly, the VMO contains the rights corresponding to the memory
/// permissions of the mapping.

View File

@ -53,7 +53,7 @@ impl<R: TRights> Vmar<TRightSet<R>> {
///
/// This method requires the following access rights:
/// 1. The VMAR contains the rights corresponding to the memory permissions of
/// the mapping. For example, if `perms` contains `VmPerm::WRITE`,
/// the mapping. For example, if `perms` contains `VmPerms::WRITE`,
/// then the VMAR must have the Write right.
/// 2. Similarly, the VMO contains the rights corresponding to the memory
/// permissions of the mapping.

View File

@ -2,7 +2,7 @@
use core::ops::Range;
use aster_frame::vm::{VmFrame, VmFrameVec, VmIo, VmMapOptions, VmPerm, VmSpace};
use aster_frame::vm::{PageFlags, VmFrame, VmFrameVec, VmIo, VmMapOptions, VmSpace};
use super::{interval::Interval, is_intersected, Vmar, Vmar_};
use crate::{
@ -51,9 +51,9 @@ struct VmMappingInner {
is_destroyed: bool,
/// The pages already mapped. The key is the page index in vmo.
mapped_pages: BTreeSet<usize>,
/// The permission of pages in the mapping.
/// All pages within the same VmMapping have the same permission.
perm: VmPerm,
/// The permissions of pages in the mapping.
/// All pages within the same VmMapping have the same permissions.
perms: VmPerms,
}
impl Interval<usize> for Arc<VmMapping> {
@ -95,7 +95,7 @@ impl VmMapping {
map_to_addr,
is_destroyed: false,
mapped_pages: BTreeSet::new(),
perm: VmPerm::from(perms),
perms,
};
Ok(Self {
@ -113,15 +113,15 @@ impl VmMapping {
fn clone_partial(
&self,
range: Range<usize>,
new_perm: Option<VmPerm>,
new_perms: Option<VmPerms>,
) -> Result<Arc<VmMapping>> {
let partial_mapping = Arc::new(self.try_clone()?);
// Adjust the mapping range and the permission.
{
let mut inner = partial_mapping.inner.lock();
inner.shrink_to(range);
if let Some(perm) = new_perm {
inner.perm = perm;
if let Some(perms) = new_perms {
inner.perms = perms;
}
}
Ok(partial_mapping)
@ -174,8 +174,8 @@ impl VmMapping {
// TODO: the current logic is vulnerable to TOCTTOU attack, since the permission may change after check.
let page_idx_range = get_page_idx_range(&(vmo_read_offset..vmo_read_offset + buf.len()));
self.check_page_idx_range(&page_idx_range)?;
let read_perm = VmPerm::R;
self.check_perm(&read_perm)?;
let read_perms = VmPerms::READ;
self.check_perms(&read_perms)?;
self.vmo.read_bytes(vmo_read_offset, buf)?;
Ok(())
@ -186,8 +186,8 @@ impl VmMapping {
let page_idx_range = get_page_idx_range(&(vmo_write_offset..vmo_write_offset + buf.len()));
self.check_page_idx_range(&page_idx_range)?;
let write_perm = VmPerm::W;
self.check_perm(&write_perm)?;
let write_perms = VmPerms::WRITE;
self.check_perms(&write_perms)?;
let mut page_addr =
self.map_to_addr() - self.vmo_offset() + page_idx_range.start * PAGE_SIZE;
@ -200,7 +200,7 @@ impl VmMapping {
// page fault before writing at the VMO to guarantee the consistency between VMO and the page table.
let need_page_fault = vm_space
.query(page_addr)?
.is_some_and(|info| !info.contains(VmPerm::W));
.is_some_and(|prop| !prop.flags.contains(PageFlags::W));
if need_page_fault {
self.handle_page_fault(page_addr, false, true)?;
}
@ -239,8 +239,8 @@ impl VmMapping {
self.vmo.check_rights(Rights::READ)?;
}
let required_perm = if write { VmPerm::W } else { VmPerm::R };
self.check_perm(&required_perm)?;
let required_perm = if write { VmPerms::WRITE } else { VmPerms::READ };
self.check_perms(&required_perm)?;
let frame = self.vmo.get_committed_frame(page_idx, write)?;
@ -257,7 +257,7 @@ impl VmMapping {
/// it should not be called during the direct iteration of the `vm_mappings`.
pub(super) fn protect(&self, new_perms: VmPerms, range: Range<usize>) -> Result<()> {
// If `new_perms` is equal to `old_perms`, `protect()` will not modify any permission in the VmMapping.
let old_perms = VmPerms::from(self.inner.lock().perm);
let old_perms = self.inner.lock().perms;
if old_perms == new_perms {
return Ok(());
}
@ -265,7 +265,7 @@ impl VmMapping {
let rights = Rights::from(new_perms);
self.vmo().check_rights(rights)?;
// Protect permission for the perm in the VmMapping.
self.protect_with_subdivision(&range, VmPerm::from(new_perms))?;
self.protect_with_subdivision(&range, new_perms)?;
// Protect permission in the VmSpace.
let vmar = self.parent.upgrade().unwrap();
let vm_space = vmar.vm_space();
@ -291,7 +291,7 @@ impl VmMapping {
map_to_addr: inner.map_to_addr,
is_destroyed: inner.is_destroyed,
mapped_pages: BTreeSet::new(),
perm: inner.perm,
perms: inner.perms,
}
};
@ -321,12 +321,16 @@ impl VmMapping {
/// Generally, this function is only used in `protect()` method.
/// This method modifies the parent `Vmar` in the end if subdividing is required.
/// It removes current mapping and add splitted mapping to the Vmar.
fn protect_with_subdivision(&self, intersect_range: &Range<usize>, perm: VmPerm) -> Result<()> {
fn protect_with_subdivision(
&self,
intersect_range: &Range<usize>,
perms: VmPerms,
) -> Result<()> {
let mut additional_mappings = Vec::new();
let range = self.range();
// Condition 4, the `additional_mappings` will be empty.
if range.start == intersect_range.start && range.end == intersect_range.end {
self.inner.lock().perm = perm;
self.inner.lock().perms = perms;
return Ok(());
}
// Condition 1 or 3, which needs an additional new VmMapping with range (range.start..intersect_range.start)
@ -342,7 +346,7 @@ impl VmMapping {
additional_mappings.push(additional_right_mapping);
}
// The protected VmMapping must exist and its range is `intersect_range`.
let protected_mapping = self.clone_partial(intersect_range.clone(), Some(perm))?;
let protected_mapping = self.clone_partial(intersect_range.clone(), Some(perms))?;
// Begin to modify the `Vmar`.
let vmar = self.parent.upgrade().unwrap();
@ -427,8 +431,8 @@ impl VmMapping {
self.inner.lock().trim_right(vm_space, vaddr)
}
fn check_perm(&self, perm: &VmPerm) -> Result<()> {
self.inner.lock().check_perm(perm)
fn check_perms(&self, perms: &VmPerms) -> Result<()> {
self.inner.lock().check_perms(perms)
}
fn check_page_idx_range(&self, page_idx_range: &Range<usize>) -> Result<()> {
@ -447,19 +451,19 @@ impl VmMappingInner {
) -> Result<()> {
let map_addr = self.page_map_addr(page_idx);
let vm_perm = {
let mut perm = self.perm;
let vm_perms = {
let mut perms = self.perms;
if is_readonly {
debug_assert!(vmo.is_cow_vmo());
perm -= VmPerm::W;
perms -= VmPerms::WRITE;
}
perm
perms
};
let vm_map_options = {
let mut options = VmMapOptions::new();
options.addr(Some(map_addr));
options.perm(vm_perm);
options.flags(vm_perms.into());
options
};
@ -514,13 +518,13 @@ impl VmMappingInner {
debug_assert!(range.end % PAGE_SIZE == 0);
let start_page = (range.start - self.map_to_addr + self.vmo_offset) / PAGE_SIZE;
let end_page = (range.end - self.map_to_addr + self.vmo_offset) / PAGE_SIZE;
let perm = VmPerm::from(perms);
let flags: PageFlags = perms.into();
for page_idx in start_page..end_page {
let page_addr = self.page_map_addr(page_idx);
if vm_space.query(page_addr)?.is_some() {
// If the page is already mapped, we will modify page table
let page_range = page_addr..(page_addr + PAGE_SIZE);
vm_space.protect(&page_range, perm)?;
vm_space.protect(&page_range, |p| p.flags = flags)?;
}
}
Ok(())
@ -581,8 +585,8 @@ impl VmMappingInner {
self.map_to_addr..self.map_to_addr + self.map_size
}
fn check_perm(&self, perm: &VmPerm) -> Result<()> {
if !self.perm.contains(*perm) {
fn check_perms(&self, perms: &VmPerms) -> Result<()> {
if !self.perms.contains(*perms) {
return_errno_with_message!(Errno::EACCES, "perm check fails");
}
Ok(())
@ -617,7 +621,7 @@ impl<R1, R2> VmarMapOptions<R1, R2> {
/// permissions.
///
/// The VMO must have access rights that correspond to the memory
/// access permissions. For example, if `perms` contains `VmPerm::Write`,
/// access permissions. For example, if `perms` contains `VmPerms::Write`,
/// then `vmo.rights()` should contain `Rights::WRITE`.
pub fn new(parent: Vmar<R1>, vmo: Vmo<R2>, perms: VmPerms) -> Self {
let size = vmo.size();