diff --git a/src/context/memory.rs b/src/context/memory.rs index a1fc1ce364..3f8c192b9a 100644 --- a/src/context/memory.rs +++ b/src/context/memory.rs @@ -103,9 +103,12 @@ impl AddrSpace { cow_file_ref.clone(), )?, + // No, your temporary UserScheme mappings will not be kept across forks. + Provider::External { is_pinned_userscheme_borrow: true, .. } => continue, + // MAP_SHARED grants are retained by reference, across address space clones (across // forks on monolithic kernels). - Provider::External { ref address_space, src_base } => Grant::borrow_grant( + Provider::External { ref address_space, src_base, .. } => Grant::borrow_grant( Arc::clone(&address_space), grant_base, grant_base, @@ -144,9 +147,15 @@ impl AddrSpace { let mapper = &mut self.table.utable; // TODO: Remove allocation (might require BTreeMap::set_key or interior mutability). - let regions = self.grants.conflicts(requested_span).map(|(base, info)| PageSpan::new(base, info.page_count)).collect::>(); + let regions = self.grants.conflicts(requested_span).map(|(base, info)| if info.is_pinned() { + Err(Error::new(EBUSY)) + } else { + Ok(PageSpan::new(base, info.page_count)) + }).collect::>(); + + for grant_span_res in regions { + let grant_span = grant_span_res?; - for grant_span in regions { let grant = self.grants.remove(grant_span.base).expect("grant cannot magically disappear while we hold the lock!"); //log::info!("Mprotecting {:#?} to {:#?} in {:#?}", grant, flags, grant_span); let intersection = grant_span.intersection(requested_span); @@ -177,17 +186,26 @@ impl AddrSpace { } Ok(()) } - pub fn munmap(mut self: RwLockWriteGuard<'_, Self>, mut requested_span: PageSpan) { + pub fn munmap(mut self: RwLockWriteGuard<'_, Self>, mut requested_span: PageSpan, unpin: bool) -> Result<()> { let mut notify_files = Vec::new(); let mut flusher = PageFlushAll::new(); let this = &mut *self; - let next = |grants: &mut UserGrants, span: PageSpan| grants.conflicts(span).map(|(base, info)| PageSpan::new(base, info.page_count)).next(); + let next = |grants: &mut UserGrants, span: PageSpan| grants.conflicts(span).map(|(base, info)| if info.is_pinned() && !unpin { + Err(Error::new(EBUSY)) + } else { + Ok(PageSpan::new(base, info.page_count)) + }).next(); - while let Some(conflicting_span) = next(&mut this.grants, requested_span) { - let grant = this.grants.remove(conflicting_span.base).expect("conflicting region didn't exist"); + while let Some(conflicting_span_res) = next(&mut this.grants, requested_span) { + let conflicting_span = conflicting_span_res?; + + let mut grant = this.grants.remove(conflicting_span.base).expect("conflicting region didn't exist"); + if unpin { + grant.info.unpin(); + } let intersection = conflicting_span.intersection(requested_span); @@ -235,6 +253,8 @@ impl AddrSpace { let _ = desc.into_inner().try_close(); } } + + Ok(()) } pub fn mmap(&mut self, page: Option, page_count: NonZeroUsize, flags: MapFlags, map: impl FnOnce(Page, PageFlags, &mut PageMapper, &mut dyn Flusher) -> Result) -> Result { // Finally, the end of all "T0DO: Abstract with other grant creation"! @@ -567,7 +587,7 @@ pub enum Provider { PhysBorrowed { base: Frame }, /// The memory is borrowed directly from another address space. - External { address_space: Arc>, src_base: Page }, + External { address_space: Arc>, src_base: Page, is_pinned_userscheme_borrow: bool }, FmapBorrowed { file_ref: GrantFileRef }, } @@ -642,6 +662,7 @@ impl Grant { provider: Provider::External { src_base, address_space: src_address_space_lock, + is_pinned_userscheme_borrow: false, } }, }) @@ -688,6 +709,7 @@ impl Grant { dst_flusher: impl Flusher, eager: bool, allow_phys: bool, + is_pinned_userscheme_borrow: bool, ) -> Result { /* if eager { @@ -729,7 +751,7 @@ impl Grant { page_count, flags, mapped: true, - provider: Provider::External { address_space: src_address_space_lock, src_base } + provider: Provider::External { address_space: src_address_space_lock, src_base, is_pinned_userscheme_borrow } }, }) } @@ -866,6 +888,8 @@ impl Grant { PageSpan::new(self.base, self.info.page_count) } pub fn extract(mut self, span: PageSpan) -> Option<(Option, Grant, Option)> { + assert!(!self.info.is_pinned(), "forgot to enforce that UserScheme mappings cannot be split"); + let (before_span, this_span, after_span) = self.span().slice(span); let before_grant = before_span.map(|span| Grant { @@ -875,9 +899,10 @@ impl Grant { mapped: self.info.mapped, page_count: span.count, provider: match self.info.provider { - Provider::External { ref address_space, src_base } => Provider::External { + Provider::External { ref address_space, src_base, .. } => Provider::External { address_space: Arc::clone(address_space), src_base, + is_pinned_userscheme_borrow: false, }, Provider::Allocated { ref cow_file_ref } => Provider::Allocated { cow_file_ref: cow_file_ref.clone() }, Provider::PhysBorrowed { ref base } => Provider::PhysBorrowed { base: base.clone() }, @@ -902,9 +927,10 @@ impl Grant { provider: match self.info.provider { // TODO: Adjust offset Provider::Allocated { ref cow_file_ref } => Provider::Allocated { cow_file_ref: cow_file_ref.clone() }, - Provider::External { ref address_space, src_base } => Provider::External { + Provider::External { ref address_space, src_base, .. } => Provider::External { address_space: Arc::clone(address_space), src_base, + is_pinned_userscheme_borrow: false, }, Provider::PhysBorrowed { base } => Provider::PhysBorrowed { base: base.next_by(this_span.count) }, @@ -920,6 +946,15 @@ impl Grant { } } impl GrantInfo { + pub fn is_pinned(&self) -> bool { + matches!(self.provider, Provider::External { is_pinned_userscheme_borrow: true, .. }) + } + pub fn unpin(&mut self) { + if let Provider::External { ref mut is_pinned_userscheme_borrow, .. } = self.provider { + *is_pinned_userscheme_borrow = false; + } + } + pub fn flags(&self) -> PageFlags { self.flags } diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index 8324432bc7..99f129a949 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -717,7 +717,7 @@ impl KernelScheme for ProcScheme { dst_addr_space.mmap(requested_dst_page, src_page_count, map.flags, |dst_page, _flags, dst_mapper, dst_flusher| Grant::transfer(middle, dst_page, src_mapper, dst_mapper, InactiveFlusher::new(), dst_flusher))? } else { - dst_addr_space.mmap(requested_dst_page, src_page_count, map.flags, |dst_page, flags, dst_mapper, flusher| Ok(Grant::borrow(Arc::clone(addrspace), src_addr_space, src_grant_span.base, dst_page, src_grant_span.count, flags, dst_mapper, flusher, true, true)?))? + dst_addr_space.mmap(requested_dst_page, src_page_count, map.flags, |dst_page, flags, dst_mapper, flusher| Ok(Grant::borrow(Arc::clone(addrspace), src_addr_space, src_grant_span.base, dst_page, src_grant_span.count, flags, dst_mapper, flusher, true, true, false)?))? }; Ok(result_base.start_address().data()) @@ -897,7 +897,8 @@ impl KernelScheme for ProcScheme { ADDRSPACE_OP_MUNMAP => { let (page, page_count) = crate::syscall::validate_region(next()??, next()??)?; - addrspace.write().munmap(PageSpan::new(page, page_count)); + let unpin = false; + addrspace.write().munmap(PageSpan::new(page, page_count), unpin)?; } ADDRSPACE_OP_MPROTECT => { let (page, page_count) = crate::syscall::validate_region(next()??, next()??)?; diff --git a/src/scheme/user.rs b/src/scheme/user.rs index b3c2d8faa9..627f8b2685 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -273,7 +273,11 @@ impl UserInner { // BARs, but it doesn't make sense yet.) let allow_phys = false; - Ok(Grant::borrow(Arc::clone(&cur_space_lock), &mut *cur_space_lock.write(), first_middle_src_page, dst_page, middle_page_count.get(), page_flags, mapper, flusher, eager, allow_phys)?) + // Deny any attempts by the scheme, to unmap these temporary pages. The only way to + // unmap them is to respond to the scheme socket. + let is_pinned_userscheme_borrow = true; + + Ok(Grant::borrow(Arc::clone(&cur_space_lock), &mut *cur_space_lock.write(), first_middle_src_page, dst_page, middle_page_count.get(), page_flags, mapper, flusher, eager, allow_phys, is_pinned_userscheme_borrow)?) })?; } @@ -579,7 +583,8 @@ impl CaptureGuard { let (first_page, page_count, _offset) = page_range_containing(self.base, self.len); - space.write().munmap(PageSpan::new(first_page, page_count)); + let unpin = true; + space.write().munmap(PageSpan::new(first_page, page_count), unpin)?; result } diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index 29cb128026..68e399c17e 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -394,7 +394,8 @@ pub fn funmap(virtual_address: usize, length: usize) -> Result { let addr_space = Arc::clone(context::current()?.read().addr_space()?); let span = PageSpan::validate_nonempty(VirtualAddress::new(virtual_address), length_aligned).ok_or(Error::new(EINVAL))?; - addr_space.write().munmap(span); + let unpin = false; + addr_space.write().munmap(span, unpin)?; Ok(0) }