mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-29 04:13:24 +00:00
Refactor the definition of page properties and permissions
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
351e08c897
commit
989970429a
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
Reference in New Issue
Block a user