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 // Check whether a given memory range is accessible by the process.
157 ///
158 /// This function ensures that system calls using memory addresses (such as
159 /// `read`, `write`, etc.) operate only on **valid and accessible**
160 /// memory regions.
161 ///
162 /// # Parameters
163 /// - `addr`: A range of virtual addresses to be accessed.
164 /// - `is_write`: `true` if the memory is being written to, `false` if it's
165 /// only being read.
166 ///
167 /// # Returns
168 /// - `true` if the memory range is valid.
169 /// - `false` if the memory range is invalid or inaccessible.
170 pub fn access_ok(&self, addr: Range<Va>, is_write: bool) -> bool {
171 todo!()
172 }
173
174 /// Wrapper function for the pager's `mmap` method. It delegates the actual
175 /// memory mapping operation to the pager's `mmap` method.
176 ///
177 /// # Parameters
178 /// - `fstate`: A mutable reference to the file state.
179 /// - `abi`: The system call ABI, which contains the arguments for the
180 /// system call.
181 ///
182 /// # Returns
183 /// - The result of the memory mapping operation, returned by the pager's
184 /// `mmap`.
185 pub fn do_mmap(
186 &mut self,
187 addr: Va,
188 size: usize,
189 prot: Permission,
190 file: Option<&RegularFile>,
191 offset: usize,
192 ) -> Result<usize, KernelError> {
193 // Calls the real implementation in pager.
194 let Self { page_table, pager } = self;
195 pager.mmap(page_table, addr, size, prot, file, offset)
196 }
197
198 /// Maps a file into the process's virtual address space.
199 ///
200 /// This function implements the `mmap` system call, which maps either an
201 /// anonymous mapping (fd = -1) or portion of a file into memory (fd >= 0).
202 /// If the mapped region represent a file content, user programs can
203 /// access the file contents as the part of the process’s memory.
204 ///
205 /// # Syscall API
206 /// ```c
207 /// void *mmap(void *addr, size_t length, int prot, int fd, off_t offset);
208 /// ```
209 /// - `addr`: Desired starting address of the mapping (must be page-aligned
210 /// and non-zero).
211 /// - `length`: Number of bytes to map (must be non-zero).
212 /// - `prot`: Desired memory protection flags.
213 /// - `fd`: File descriptor of the file to be mapped.
214 /// - `offset`: Offset in the file where mapping should begin.
215 ///
216 /// # Arguments
217 ///
218 /// * `fstate` - Mutable reference to the current file state.
219 /// * `abi` - A reference to the system call arguments, including the file
220 /// descriptor, mapping length, protection flags, and file offset.
221 ///
222 /// # Behavior
223 ///
224 /// This function performs validation on the provided arguments before
225 /// forwarding the request to the pager’s `mmap` method. The following
226 /// conditions must be met:
227 ///
228 /// - `addr` must be non-zero and page-aligned.
229 /// - `length` must be non-zero.
230 /// - The file descriptor must refer to a regular file or -1 for anonymous
231 /// mapping.
232 /// - The mapping must not overlap with any already mapped region, including
233 /// the user stack or any memory occupied by the program binary.
234 ///
235 /// Unlike Linux, KeOS does not support automatic address selection for
236 /// `addr == NULL`, so `mmap` fails if `addr` is zero.
237 ///
238 /// If the length of the file is not a multiple of the page size, any excess
239 /// bytes in the final page are zero-filled.
240 ///
241 /// # Returns
242 ///
243 /// Returns the starting virtual address of the mapped region on success, or
244 /// a [`KernelError`] on failure due to invalid arguments or conflicts with
245 /// existing memory mappings.
246 pub fn mmap(
247 &mut self,
248 fstate: &mut FileStruct,
249 abi: &SyscallAbi,
250 ) -> Result<usize, KernelError> {
251 self.do_mmap(todo!(), todo!(), todo!(), todo!(), todo!())
252 }
253
254 /// Unmaps a memory-mapped file region.
255 ///
256 /// This function implements the `munmap` system call, which removes a
257 /// previously established memory mapping created by `mmap`. It releases
258 /// the virtual memory associated with the mapping.
259 ///
260 /// # Syscall API
261 /// ```c
262 /// int munmap(void *addr);
263 /// ```
264 /// - `addr`: The starting virtual address of the mapping to unmap. This
265 /// must match the address returned by a previous call to `mmap` by the
266 /// same process and must not have been unmapped already.
267 ///
268 /// # Arguments
269 ///
270 /// * `fstate` - Mutable reference to the current file state.
271 /// * `abi` - A reference to the system call arguments, including the
272 /// address to unmap.
273 ///
274 /// # Behavior
275 ///
276 /// - Unmaps the virtual memory region starting at `addr` that was
277 /// previously mapped via `mmap`.
278 /// - Unmodified pages are simply discarded.
279 /// - The virtual pages corresponding to the mapping are removed from the
280 /// process's address space.
281 ///
282 /// # Additional Notes
283 ///
284 /// - Calling `close` on a file descriptor or removing the file from the
285 /// filesystem does **not** unmap any of its active mappings.
286 /// - To follow the Unix convention, mappings remain valid until they are
287 /// explicitly unmapped via `munmap`.
288 ///
289 /// # Returns
290 ///
291 /// Returns `Ok(0)` on success or a [`KernelError`] if the address is
292 /// invalid or does not correspond to an active memory mapping.
293 pub fn munmap(&mut self, abi: &SyscallAbi) -> Result<usize, KernelError> {
294 // Calls the pager's munmap method with placeholders for arguments.
295 self.pager.munmap(&mut self.page_table, todo!())
296 }
297
298 /// Find a mapped page at the given virtual address and apply a function to
299 /// it.
300 ///
301 /// This function searches for a memory page mapped at `addr` and, if found,
302 /// applies the provided function `f` to it. The function `f` receives a
303 /// [`PageRef`] to the page and its corresponding [`Permission`] flags.
304 ///
305 /// # Parameters
306 /// - `addr`: The virtual address ([`Va`]) of the page to find.
307 /// - `f`: A closure that takes a [`PageRef`] and its [`Permission`] flags,
308 /// returning a value of type `R`.
309 ///
310 /// # Returns
311 /// - `Some(R)`: If the page is found, the function `f` is applied, and its
312 /// result is returned.
313 /// - `None`: If no mapped page is found at `addr`.
314 ///
315 /// # Usage
316 /// This method allows safe access to a mapped page. It is useful for
317 /// performing read operations or permission checks on a page.
318 ///
319 /// # Safety
320 /// - The rust's lifetime guarantees that the closure `f` never stores the
321 /// [`PageRef`] beyond its invocation.
322 pub fn get_user_page_and<R>(
323 &mut self,
324 addr: Va,
325 f: impl FnOnce(PageRef, Permission) -> R,
326 ) -> Result<R, KernelError> {
327 let Self { page_table, pager } = self;
328 if let Some((pgref, perm)) = pager.get_user_page(page_table, addr) {
329 Ok(f(pgref, perm))
330 } else {
331 Err(KernelError::BadAddress)
332 }
333 }
334}