keos_project2/mm_struct.rs
1//! # Memory State of a process
2//!
3//! In the project 1, you implemented the file state management for a
4//! process. An equally important state of a process is memory state.
5//! This memory state involves managing virtual memory regions, tracking memory
6//! allocations, and implementing memory deallocation when memory is no longer
7//! needed. The operating system must track all active mappings per process,
8//! enforce correct access permissions, and ensure proper cleanup when memory is
9//! unmapped.
10//!
11//! ## Memory in KeOS
12//!
13//! The state of a process's memory is represented by the [`MmStruct`]
14//! structure, similiar to the Linux kernel's `struct mm_struct`. Each process
15//! maintains an [`MmStruct`] instance, which tracks the memory mapping state
16//! for a process. This state plays a central role for serving a memory mapping
17//! system calls: **mmap** and **munmap**.
18//!
19//! Memory mapping (`mmap`) allows processes to allocate, share, or access
20//! memory in a flexible way. It is commonly used for:
21//! - Allocating memory dynamically without relying on heap or stack growth.
22//! - Mapping files into memory for fast access.
23//! - Sharing memory between processes.
24//!
25//! The `mmap` system call establishes a mapping between a process's virtual
26//! address space and either physical memory or a file. These memory mappings
27//! are recorded within the **page table**, similar to how regular memory pages
28//! are managed. However, unlike normal heap allocations, `mmap`-based
29//! allocations allow more control over page protection, mapping policies, and
30//! address alignment. When a process accesses an address within the mapped
31//! region, a **page fault** occurs, triggering the operating system to allocate
32//! and map the corresponding physical pages.
33//!
34//! The [`MmStruct`] contains the two key components:
35//! - **Page Table**: Tracks mappings between virtual and physical addresses.
36//! - **Pager**: Defines the policy for memory mapping. and unmapping.`
37//!
38//! ### Validating User Input
39//!
40//! One of the important aspects of managing memory safely is ensuring that
41//! user input, such as memory addresses provided by system calls, is validated
42//! correctly. **The kernel must never crash due to user input.**
43//!
44//! Many system calls, like `read` and `write`, rely on user-supplied memory
45//! addresses—typically as buffer pointers. These addresses need to be carefully
46//! validated before they can be used to ensure the integrity and stability of
47//! the system.
48//!
49//! Without proper validation, several serious problems may arise:
50//! - **Unauthorized access to kernel memory**: If the user is allowed to access
51//! kernel memory, this can lead to privilege escalation vulnerabilities,
52//! potentially giving the user more control than intended.
53//! - **Dereferencing invalid pointers**: Accessing uninitialized or incorrectly
54//! mapped memory can result in segmentation faults or undefined behavior,
55//! leading to crashes or unexpected outcomes.
56//! - **Memory corruption**: Improper handling of memory can result in
57//! corrupting the system's memory state, which can affect other processes,
58//! crash the kernel, or destabilize the entire system.
59//!
60//! [`MmStruct`] mitigate these risks with the [`MmStruct::access_ok`] method,
61//! which ensures that the memory addresses provided by system calls are **valid
62//! and safe** before being used. This validation mechanism will
63//! prevent dangerous operations by checking if memory access is allowed, based
64//! on the current process's memory layout and protection policies.
65//!
66//! By incorporating proper validation, [`MmStruct::access_ok`] ensures that
67//! invalid memory accesses are handled **gracefully**, returning appropriate
68//! errors rather than causing kernel panics or undefined behavior. This
69//! validation mechanism plays a crucial role in maintaining system integrity
70//! and protecting the kernel from potential vulnerabilities.
71
72//! ### `Pager`
73//!
74//! The actual behavior of [`MmStruct`] lies in the [`Pager`] trait.
75//! When a user program invokes those system calls, the [`MmStruct`] parses the
76//! arguements from the [`SyscallAbi`] and forwarded them to an implementation
77//! of the [`Pager`] traits, which provides the core interface for handling
78//! memory mappings. Similarly, the core implementation to validate memory also
79//! lies on the [`Pager::access_ok`].
80//!
81//! ## Implementation Requirements
82//! You need to implement the followings:
83//! - [`MmStruct::mmap`]
84//! - [`MmStruct::munmap`]
85//! - [`MmStruct::access_ok`]
86//!
87//! After implementing them, move on to the next [`section`] to implement
88//! paging policy, called `EagerPager`.
89//!
90//! [`section`]: crate::eager_pager
91
92use crate::{page_table::PageTable, pager::Pager};
93use core::ops::Range;
94use keos::{
95 KernelError,
96 addressing::Va,
97 fs::RegularFile,
98 mm::{PageRef, page_table::Permission},
99};
100use keos_project1::{file_struct::FileStruct, syscall::SyscallAbi};
101
102/// The [`MmStruct`] represents the memory state for a specific process,
103/// corresponding to the Linux kernel's `struct mm_struct`.
104///
105/// This struct encapsulates the essential information required to manage
106/// a process's virtual memory, including its page table and the pager
107/// responsible for handling memory mapping operations (such as `mmap` and
108/// `munmap`).
109///
110/// The [`MmStruct`] ensures that memory-related system calls and operations are
111/// correctly applied within the process’s address space. It provides mechanisms
112/// to allocate, map, and unmap memory pages, and serves as the interface
113/// through which the OS kernel interacts with the user process’s memory layout.
114///
115/// # Memory State
116///
117/// The memory state includes the page table (referenced by `page_table_addr`)
118/// that manages the virtual-to-physical address translations for the process,
119/// and a pager (`pager`) that defines how memory-mapped files and dynamic
120/// memory allocations are handled. Together, these components allow each
121/// process to maintain its own isolated memory environment, supporting both
122/// memory protection and efficient address space management.
123///
124/// Like its Linux counterpart, [`MmStruct`] plays a central role in memory
125/// management, providing the kernel with per-process control over virtual
126/// memory.
127pub struct MmStruct<P: Pager> {
128 /// The page table that maintains mappings between virtual and physical
129 /// addresses.
130 pub page_table: PageTable,
131
132 /// The pager that handles memory allocation (`mmap`) and deallocation
133 /// (`munmap`).
134 pub pager: P,
135}
136
137impl<P: Pager> Default for MmStruct<P> {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143impl<P: Pager> MmStruct<P> {
144 /// Creates a new [`MmStruct`] with an empty page table and a new pager
145 /// instance.
146 ///
147 /// # Returns
148 /// - A new [`MmStruct`] instance initialized with a new [`PageTable`] and
149 /// `P::new()`.
150 pub fn new() -> Self {
151 Self {
152 page_table: PageTable::new(),
153 pager: P::new(), // Initialize the pager.
154 }
155 }
156
157 // Check whether a given memory range is accessible by the process.
158 ///
159 /// This function ensures that system calls using memory addresses (such as
160 /// `read`, `write`, etc.) operate only on **valid and accessible**
161 /// memory regions.
162 ///
163 /// # Parameters
164 /// - `addr`: A range of virtual addresses to be accessed.
165 /// - `is_write`: `true` if the memory is being written to, `false` if it's
166 /// only being read.
167 ///
168 /// # Returns
169 /// - `true` if the memory range is valid.
170 /// - `false` if the memory range is invalid or inaccessible.
171 pub fn access_ok(&self, addr: Range<Va>, is_write: bool) -> bool {
172 todo!()
173 }
174
175 /// Wrapper function for the pager's `mmap` method. It delegates the actual
176 /// memory mapping operation to the pager's `mmap` method.
177 ///
178 /// # Parameters
179 /// - `fstate`: A mutable reference to the file state.
180 /// - `abi`: The system call ABI, which contains the arguments for the
181 /// system call.
182 ///
183 /// # Returns
184 /// - The result of the memory mapping operation, returned by the pager's
185 /// `mmap`.
186 pub fn do_mmap(
187 &mut self,
188 addr: Va,
189 size: usize,
190 prot: Permission,
191 file: Option<&RegularFile>,
192 offset: usize,
193 ) -> Result<usize, KernelError> {
194 // Calls the real implementation in pager.
195 let Self { page_table, pager } = self;
196 pager.mmap(page_table, addr, size, prot, file, offset)
197 }
198
199 /// Maps a file into the process's virtual address space.
200 ///
201 /// This function implements the `mmap` system call, which maps either an
202 /// anonymous mapping (fd = -1) or portion of a file into memory (fd >= 0).
203 /// If the mapped region represent a file content, user programs can
204 /// access the file contents as the part of the process’s memory.
205 ///
206 /// # Syscall API
207 /// ```c
208 /// void *mmap(void *addr, size_t length, int prot, int fd, off_t offset);
209 /// ```
210 /// - `addr`: Desired starting address of the mapping (must be page-aligned
211 /// and non-zero).
212 /// - `length`: Number of bytes to map (must be non-zero).
213 /// - `prot`: Desired memory protection flags.
214 /// - `fd`: File descriptor of the file to be mapped.
215 /// - `offset`: Offset in the file where mapping should begin.
216 ///
217 /// # Arguments
218 ///
219 /// * `fstate` - Mutable reference to the current file state.
220 /// * `abi` - A reference to the system call arguments, including the file
221 /// descriptor, mapping length, protection flags, and file offset.
222 ///
223 /// # Behavior
224 ///
225 /// This function performs validation on the provided arguments before
226 /// forwarding the request to the pager’s `mmap` method. The following
227 /// conditions must be met:
228 ///
229 /// - `addr` must be non-zero and page-aligned.
230 /// - `length` must be non-zero.
231 /// - The file descriptor must refer to a regular file or -1 for anonymous
232 /// mapping.
233 /// - The mapping must not overlap with any already mapped region, including
234 /// the user stack or any memory occupied by the program binary.
235 ///
236 /// Unlike Linux, KeOS does not support automatic address selection for
237 /// `addr == NULL`, so `mmap` fails if `addr` is zero.
238 ///
239 /// If the length of the file is not a multiple of the page size, any excess
240 /// bytes in the final page are zero-filled.
241 ///
242 /// # Returns
243 ///
244 /// Returns the starting virtual address of the mapped region on success, or
245 /// a [`KernelError`] on failure due to invalid arguments or conflicts with
246 /// existing memory mappings.
247 pub fn mmap(
248 &mut self,
249 fstate: &mut FileStruct,
250 abi: &SyscallAbi,
251 ) -> Result<usize, KernelError> {
252 self.do_mmap(todo!(), todo!(), todo!(), todo!(), todo!())
253 }
254
255 /// Unmaps a memory-mapped file region.
256 ///
257 /// This function implements the `munmap` system call, which removes a
258 /// previously established memory mapping created by `mmap`. It releases
259 /// the virtual memory associated with the mapping.
260 ///
261 /// # Syscall API
262 /// ```c
263 /// int munmap(void *addr);
264 /// ```
265 /// - `addr`: The starting virtual address of the mapping to unmap. This
266 /// must match the address returned by a previous call to `mmap` by the
267 /// same process and must not have been unmapped already.
268 ///
269 /// # Arguments
270 ///
271 /// * `fstate` - Mutable reference to the current file state.
272 /// * `abi` - A reference to the system call arguments, including the
273 /// address to unmap.
274 ///
275 /// # Behavior
276 ///
277 /// - Unmaps the virtual memory region starting at `addr` that was
278 /// previously mapped via `mmap`.
279 /// - Unmodified pages are simply discarded.
280 /// - The virtual pages corresponding to the mapping are removed from the
281 /// process's address space.
282 ///
283 /// # Additional Notes
284 ///
285 /// - Calling `close` on a file descriptor or removing the file from the
286 /// filesystem does **not** unmap any of its active mappings.
287 /// - To follow the Unix convention, mappings remain valid until they are
288 /// explicitly unmapped via `munmap`.
289 ///
290 /// # Returns
291 ///
292 /// Returns `Ok(0)` on success or a [`KernelError`] if the address is
293 /// invalid or does not correspond to an active memory mapping.
294 pub fn munmap(&mut self, abi: &SyscallAbi) -> Result<usize, KernelError> {
295 // Calls the pager's munmap method with placeholders for arguments.
296 self.pager.munmap(&mut self.page_table, todo!())
297 }
298
299 /// Find a mapped page at the given virtual address and apply a function to
300 /// it.
301 ///
302 /// This function searches for a memory page mapped at `addr` and, if found,
303 /// applies the provided function `f` to it. The function `f` receives a
304 /// [`PageRef`] to the page and its corresponding [`Permission`] flags.
305 ///
306 /// # Parameters
307 /// - `addr`: The virtual address ([`Va`]) of the page to find.
308 /// - `f`: A closure that takes a [`PageRef`] and its [`Permission`] flags,
309 /// returning a value of type `R`.
310 ///
311 /// # Returns
312 /// - `Some(R)`: If the page is found, the function `f` is applied, and its
313 /// result is returned.
314 /// - `None`: If no mapped page is found at `addr`.
315 ///
316 /// # Usage
317 /// This method allows safe access to a mapped page. It is useful for
318 /// performing read operations or permission checks on a page.
319 ///
320 /// # Safety
321 /// - The rust's lifetime guarantees that the closure `f` never stores the
322 /// [`PageRef`] beyond its invocation.
323 pub fn get_user_page_and<R>(
324 &mut self,
325 addr: Va,
326 f: impl FnOnce(PageRef, Permission) -> R,
327 ) -> Result<R, KernelError> {
328 let Self { page_table, pager } = self;
329 if let Some((pgref, perm)) = pager.get_user_page(page_table, addr) {
330 Ok(f(pgref, perm))
331 } else {
332 Err(KernelError::BadAddress)
333 }
334 }
335}