diff --git a/src/context/memory.rs b/src/context/memory.rs
index 09e0f4027b5310b2765aaf65c0eebf0930a90c38..36b447b70f7c4db200d64a9d284b5990424894fa 100644
--- a/src/context/memory.rs
+++ b/src/context/memory.rs
@@ -34,8 +34,6 @@ pub fn map_flags(page_flags: PageFlags<RmmA>) -> MapFlags {
     let mut flags = MapFlags::PROT_READ;
     if page_flags.has_write() { flags |= MapFlags::PROT_WRITE; }
     if page_flags.has_execute() { flags |= MapFlags::PROT_EXEC; }
-    // TODO: MAP_SHARED/MAP_PRIVATE (requires that grants keep track of what they borrow and if
-    // they borrow shared or CoW).
     flags
 }
 
diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs
index 41537b5ab727e2662df8069e1fc93c5b6b127e88..74aff40cc7d968feb4dcc262df688026a05ec9aa 100644
--- a/src/scheme/proc.rs
+++ b/src/scheme/proc.rs
@@ -592,7 +592,7 @@ impl Scheme for ProcScheme {
 
         match handle.info.operation {
             Operation::AwaitingAddrSpaceChange { new, new_sp, new_ip } => {
-                stop_context(handle.info.pid, |context: &mut Context| unsafe {
+                let prev_addr_space = stop_context(handle.info.pid, |context: &mut Context| unsafe {
                     if let Some(saved_regs) = ptrace::regs_for_mut(context) {
                         #[cfg(target_arch = "aarch64")]
                         {
@@ -615,9 +615,7 @@ impl Scheme for ProcScheme {
                         context.clone_entry = Some([new_ip, new_sp]);
                     }
 
-                    let _prev_addr_space = context.set_addr_space(new);
-
-                    Ok(())
+                    Ok(context.set_addr_space(new))
                 })?;
                 let _ = ptrace::send_event(crate::syscall::ptrace_event!(PTRACE_EVENT_ADDRSPACE_SWITCH, 0));
             }
diff --git a/src/scheme/user.rs b/src/scheme/user.rs
index 62db18104fec289efd966ce6be95aa26ef7d4ba2..b0a1c0907680fbec5a8523f1426b9710d0a84866 100644
--- a/src/scheme/user.rs
+++ b/src/scheme/user.rs
@@ -543,10 +543,15 @@ impl UserInner {
         };
 
         let page_count_nz = NonZeroUsize::new(page_count).expect("already validated map.size != 0");
-        let dst_base = dst_addr_space.write().mmap(dst_base, page_count_nz, map.flags | MAP_FIXED_NOREPLACE, &mut Vec::new(), |dst_base, flags, mapper, flusher| {
+        let mut notify_files = Vec::new();
+        let dst_base = dst_addr_space.write().mmap(dst_base, page_count_nz, map.flags | MAP_FIXED_NOREPLACE, &mut notify_files, |dst_base, flags, mapper, flusher| {
             Grant::borrow_fmap(PageSpan::new(dst_base, page_count), page_flags(map.flags), file_ref, src, mapper, flusher)
         })?;
 
+        for map in notify_files {
+            let _ = map.unmap();
+        }
+
         Ok(dst_base.start_address().data())
     }
 }
@@ -829,6 +834,20 @@ impl KernelScheme for UserScheme {
 
         inner.fmap_inner(Arc::clone(addr_space), file, map)
     }
+    fn kfunmap(&self, number: usize, offset: usize, size: usize) -> Result<()> {
+        let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
+
+        let res = inner.call_extended(CallerCtx {
+            pid: context::context_id().into(),
+            uid: offset as u32,
+            gid: (offset >> 32) as u32,
+        }, [KSMSG_MUNMAP, number, size, 0])?;
+
+        match res {
+            Response::Regular(_) => Ok(()),
+            Response::Fd(_) => Err(Error::new(EIO)),
+        }
+    }
 
     fn as_user_inner(&self) -> Option<Result<Arc<UserInner>>> {
         Some(self.inner.upgrade().ok_or(Error::new(ENODEV)))
diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs
index 68e399c17ed35f4bea486053293c51fb9caaed37..cc97aee39a574fa2ca6e47203a2c2767732710da 100644
--- a/src/syscall/fs.rs
+++ b/src/syscall/fs.rs
@@ -387,6 +387,12 @@ pub fn fstat(fd: FileHandle, user_buf: UserSliceWo) -> Result<usize> {
 }
 
 pub fn funmap(virtual_address: usize, length: usize) -> Result<usize> {
+    // Partial lengths in funmap are allowed according to POSIX, but not particularly meaningful;
+    // since the memory needs to SIGSEGV if later read, the entire page needs to disappear.
+    //
+    // Thus, while (temporarily) allowing unaligned lengths for compatibility, aligning the length
+    // should be done by libc.
+
     let length_aligned = length.next_multiple_of(PAGE_SIZE);
     if length != length_aligned {
         log::warn!("funmap passed length {:#x} instead of {:#x}", length, length_aligned);
@@ -395,7 +401,11 @@ pub fn funmap(virtual_address: usize, length: usize) -> Result<usize> {
     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))?;
     let unpin = false;
-    addr_space.write().munmap(span, unpin)?;
+    let notify = addr_space.write().munmap(span, unpin)?;
+
+    for map in notify {
+        let _ = map.unmap();
+    }
 
     Ok(0)
 }
diff --git a/syscall b/syscall
index ff149946c07628dde39db05997e221b5024ccb95..ed28083b73ed2b163e2d64ee3753f1d6ff6c94d1 160000
--- a/syscall
+++ b/syscall
@@ -1 +1 @@
-Subproject commit ff149946c07628dde39db05997e221b5024ccb95
+Subproject commit ed28083b73ed2b163e2d64ee3753f1d6ff6c94d1