From af36ae12b013c8ed0e8bcb34afd1e43669326240 Mon Sep 17 00:00:00 2001
From: Anhad Singh <andypython@protonmail.com>
Date: Wed, 11 Dec 2024 16:29:17 +1100
Subject: [PATCH] fix(mremap): fix referencing

Before it was first add_ref'ed by `borrow_frame_enforce_rw_allocated`,
manually and then by `allocated_shared_one_page`.

Now it is only done by `borrow_frame_enforce_rw_allocated` and does not
get unref-ed as take() is called on the returned `RaiiFrame`.

Now the page is manually mapped and an `Allocated` type grant is
constructed (synonymous to `MAP_PRIVATE`). Before by using `allocated_shared_one_page`
an `AllocatedShared` provided grant was constructed (synonymous to
`MAP_SHARED`), which was wrong as the TCB would've not got CoW-ed
after fork(), making the Tcb malformed.

Signed-off-by: Anhad Singh <andypython@protonmail.com>
---
 src/context/memory.rs | 16 ++++++++++++++++
 src/syscall/fs.rs     | 34 +++++++++++++++-------------------
 2 files changed, 31 insertions(+), 19 deletions(-)

diff --git a/src/context/memory.rs b/src/context/memory.rs
index 615b6108..890eb5b2 100644
--- a/src/context/memory.rs
+++ b/src/context/memory.rs
@@ -1102,6 +1102,22 @@ pub struct GrantFileRef {
 impl Grant {
     // TODO: PageCount newtype, to avoid confusion between bytes and pages?
 
+    // `base` must be mapped by the caller.
+    pub fn allocated_one_page_nomap(base: Page, flags: PageFlags<RmmA>) -> Grant {
+        Grant {
+            base,
+            info: GrantInfo {
+                page_count: 1,
+                flags,
+                mapped: true,
+                provider: Provider::Allocated {
+                    cow_file_ref: None,
+                    phys_contiguous: false,
+                },
+            },
+        }
+    }
+
     // TODO: is_pinned
     pub fn allocated_shared_one_page(
         frame: Frame,
diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs
index cab3c3c1..62f0753c 100644
--- a/src/syscall/fs.rs
+++ b/src/syscall/fs.rs
@@ -9,10 +9,9 @@ use crate::{
     context::{
         self,
         file::{FileDescription, FileDescriptor, InternalFlags},
-        memory::{AddrSpace, PageSpan},
+        memory::{AddrSpace, GenericFlusher, Grant, PageSpan, TlbShootdownActions},
         process,
     },
-    memory::AddRefError,
     paging::{Page, VirtualAddress, PAGE_SIZE},
     scheme::{self, CallerCtx, FileHandle, KernelScheme, OpenResult},
     syscall::{data::Stat, error::*, flag::*},
@@ -500,16 +499,8 @@ pub fn mremap(
         if new_page_count != 1 {
             return Err(Error::new(EOPNOTSUPP));
         }
-        let raii_frame = addr_space.borrow_frame_enforce_rw_allocated(src_span.base)?;
-
-        let info = crate::memory::get_page_info(raii_frame.get()).ok_or(Error::new(EINVAL))?;
 
-        match info.add_ref(crate::memory::RefKind::Shared) {
-            Ok(()) => (),
-            Err(AddRefError::RcOverflow) => return Err(Error::new(ENOMEM)),
-            Err(AddRefError::CowToShared) => return Err(Error::new(EINVAL)),
-            Err(AddRefError::SharedToCow) => unreachable!(),
-        }
+        let raii_frame = addr_space.borrow_frame_enforce_rw_allocated(src_span.base)?;
 
         let base = addr_space.acquire_write().mmap(
             &addr_space,
@@ -518,14 +509,19 @@ pub fn mremap(
             map_flags,
             &mut Vec::new(),
             |page, page_flags, mapper, flusher| {
-                crate::context::memory::Grant::allocated_shared_one_page(
-                    raii_frame.take(),
-                    page,
-                    page_flags,
-                    mapper,
-                    flusher,
-                    false,
-                )
+                let frame = raii_frame.take();
+                // XXX: add_ref(RefKind::Shared) is internally done by borrow_frame_enforce_rw_allocated(src_span.base).
+                // The page does not get unref-ed as we call take() on the `raii_frame`.
+                unsafe {
+                    mapper
+                        .map_phys(page.start_address(), frame.base(), page_flags)
+                        .ok_or(Error::new(ENOMEM))?
+                        .ignore();
+
+                    flusher.queue(frame, None, TlbShootdownActions::NEW_MAPPING);
+                }
+
+                Ok(Grant::allocated_one_page_nomap(page, page_flags))
             },
         )?;
 
-- 
GitLab