CS962: Operating System Principles Cyber Security
Posted: March 15th, 2024
Programming Assignment 2
(GemOS Kernel Programming)
CS962: Operating System Principles
2023-24 Q3 eMasters in Cyber Security
Department of Computer Science and Engineering
Due Date: 06th March 2024, 11:55pm
Introduction
The objective of this assignment is to expose you to OS memory management concepts covered in the lectures. You will be implementing memory-related system calls in gemOS.
The gemOS source can be found in the gemOS/src directory. Structure of the gemOS/src directory is as following:
• gemOS/src/user/ contains the user space components of the gemOS.
– init.c is the userspace program that will run on gemOS. This program will interact with gemOS using system calls.
– lib.c is library that contains the implementation of common functions such as printf(). It is equivalent to the C library (libc) in Linux. (Not to be modified)
– ulib.h is a header file containing declaration of various functions and system calls. It is equivalent to the user space header files in Linux. (Not to be modified)
• gemOS/src/include/ contains the header files related to the kernel space implementation of the gemOS. (Not to be modified)
• gemOS/src/*.c, *.o files contain the implementation of the kernel space of the gemOS. (Modify only the specified files)
• gemOS/src/Makefile is used to build the gemOS kernel binary gemOS.kernel. (Not to be modified).
Background
This section revisits some known along with additional concepts required to finish this assignment.
VM Area
A VM Area is a contiguous region in the virtual address space of a process. Each area is associated with a start address, end address, and some protection information. The mmap system call is used to allocate VM Areas.
Paging, TLBs, and Efficiency
You are familiar with the concept of multi-level paging. Most operating systems implement pages of 4KB and the mappings between virtual pages and physical frames are stored in the page tables. Further, to speed up the process of virtual-to-physical translations, some of these mappings are stored in the TLB, a high speed, limited-capacity cache. Hence, each entry in the TLB essentially provides information about 4KB memory. Figure 1 depicts the addressing scheme for 4KB pages. Figure 2 depicts the contents of a Page Table Entry in 4 level paging scheme.
Figure 1: Address Translation for 4KB pages
Figure 2: Structure of PTE at each level of the page table (only relevant fields are shown)
Task 1: Lets Print Virtual Memory Mappings [20 Points]
In this task you will implement functionality equivalent to Linux’s /proc/[pid]/maps inside GemOS. Linux’s /proc/[pid]/maps allows users to list virtual memory (VM) layout of a process, for eg: if pid of a process is 1234, open /proc/1234/maps. The VM layout of a process is part of its execution context (Figure 3).
Figure 3 shows the execution context (struct exec context) in GemOS (include/context.h). The execution context points (struct mm segment mms) to different virtual memory segments such as CODE, DATA, STACK etc. The virtual memory areas (vm area) allocated using dynamic memory allocation system call mmap (MMAP) is a list of vm areas and vm area member (struct vm area *vm area) in exec context points to this list as shown in Figure 3.
Figure 3: GemOS execution context (exec context)
Implementation
• The system call and user space library wrapper for kernel function print pmaps(struct exec context *ctx) in mprotect.c is already implemented for you in user/lib.c. pmaps()
is the user space library wrapper and it triggers print pmaps in kernel. You can call pmaps() from user space i.e. from /user/init.c.
• You need to complete print pmaps(struct exec context *ctx) kernel function implementation in mprotect.c file, where ctx points to the execution ccontext of the process that made the pmap() system call, using which you can access it’s VM layout.
• The output should be printed in a format start end permissions name
where start, end corresponds to starting, ending address of VM area, permissions corresponds to read (R), write (W), execute (X) permissions of the VM area (mention – for any missing permission) and name corresponds to one of these CODE, RODATA, DATA, MMAP, STACK based on VM area. An example of output format is illustrated below.
0x100000000 0x100008000 R-X CODE
0x180000000 0x180001000 R-X DATA
0x180200000 0x180201000 R-X MMAP
0x180201000 0x180202000 -W- MMAP
0x7FFFFE000 0x800000000 RW- STACK
• The access flags field of struct mm segment and struct vm area in include/context.h contains permissions information of respective virtual memory layouts.
• Sample test cases are provided in /user/testcases/task1. You need to copy test case file to /user/init.c before compiling gemOS.kernel using make in gemOS/src folder.
Task 2: Page Table Manipulations [40 marks]
This part of the assignment, when implemented correctly, will enable lazy allocation support in gemOS. As discussed in class, lazy allocation delays the physical memory allocation corresponding to a virtual address till the virtual address gets accessed (i.e., read or write operation is performed on that address) for the first time.
long vm area pagefault(struct exec context *current, u64 addr, int error code)
current: exec context of the process whose execution resulted in the page fault address: Accessed virtual address that resulted in the page fault error code: Provides the information about the fault such as whether the fault occurred in userspace execution or kernel mode execution, type of access i.e, read or write etc. The error codes for various faults are explained in Section 1.
Description: We have provided a template function vm area pagefault() (in src/mprotect.c) which gets called when a page fault occurs for an address in [MMAP AREA START, MMAP AREA END] (defined in include/mmap.h). When this page fault handler is called, you have to iterate through the list of VMAs of the process (whose exec context is passed as an argument to this function) to check validity of the address. In the validity checks, verify the faulting address belongs to some VMA and the access matches with the protection flags of the VMA. The following scenarios should be handled by the page fault handler.
• Page fault can arise due to read (error code = 0x4) or write (error code = 0x6) access to an address such that a VMA exists corresponding to the accessed address and no physical page is mapped. Note that write access on an address such that VMA corresponding the address has only read permission should be flagged as an invalid access. All other combinations of accesses and VMA protection flags are considered as valid accesses. For example, read access on an address such that VMA corresponding the address has only write permission is a valid access. For a valid access, you need to do the following,
– Allocate a new physical page frame (PFN), set access flags and update the page table entry(s) of the faulting address. Note that pgd member of a process’s exec context can be used to find the virtual address of the PGD level of the page table using osmap(ctx- pgd).
– You have to structure the PTE entry(s) (Figure 2) at different levels depending on the access flags of the virtual address that is being mapped.
• A page fault can arise due to a read/write access such that no VMA exists corresponding the accessed address. Such an access should be handled as an invalid access.
• A page fault arising due to a write access to a page marked as READ ONLY (error code = 0x7). In this case, if VMA corresponding the page fault address does not have write permission, flag this access as invalid.
Return Value After fixing fault for a valid access, return 1. For an invalid access return -1.
Notes
• The pgd of exec context contains the PFN of the PGD level in the page table for the process.
• You can assume that fork()/vfork()/cfork()/clone() will not be called in the test-cases for this part of the assignment.
• You are free to create your own helper functions in gemOS/src/mprotect.c
Testing
The user space program code is available in gemOS/src/user/init.c. You need to write your test cases in init.c to validate your implementation. The sample test cases in user/testcases/task2 can be copied into init.c to make use of them.
Task 3: Change Virtual Memory Area Protection [40 marks]
In this task, you need to implement mprotect. The mprotect() system call changes the access protections for the calling process’s memory pages containing any part of the address range in the interval [addr, addr+length-1].
int mprotect(void *addr, int length, int prot)
addr: The addr argument specifies the start address of the memory mapped region whose protection needs to be changed. You can assume that it will be page aligned.
length: The length argument specifies the length of the mapping (starting from addr) whose protection flags needs to be changed. It will be greater than 0 and need not be a multiple of page size. Length is in bytes.
prot: The prot argument describes the desired memory protection of the mapping. It can be one of the following flags:
• PROT READ – The protection of the mapping vm area is set to read only. The physical pages which map to this vm area are also set to read only. Any attempt to write to pages with read only permission results in SIGSEGV.
• PROT READ | PROT WRITE – The protection of the mapping vm area is set to both read and write. Physical pages corresponding this vm area, will have both read and write access.
System call handler: To implement mprotect() system call, you are required to provide implementation for the template function long vm area mprotect(struct exec context *current, u64 addr, int length, int prot) (in src/mprotect.c). Note that the system call handler is passed one extra argument (the exec context of the current process).
Description:
You can assume that mprotect() will be used to change the access protection of VMAs only. Based on the arguments passed to the mprotect(), you have to iterate through the list of VMAs of the current process and modify the access permissions. mprotect might create (an existing VMA might be partitioned into multiple VMA’s), expand (after change in protections, a VMA might get merged with adjacent VMAs) or shrink (an existing VMA might be partitioned into multiple VMAs) the vm area mapping. Refer Figure 4. Changing the protection of even a single byte of a page changes the protection of the entire page. It is not an error if the region whose protections need to be changed doesn’t contain any VMAs.
Figure 4: Impact of mprotect on VMA’s (assume PROT WRITE means PROT READ|PROT WRITE)
When protections of a virtual address region is changed using mprotect, you should update the virtual to physical translation in page table to enforce the change in access permissions.
Return Value On success, return 0. On failure, return -1.
Notes
• The address range for will always be between MMAP AREA START – MMAP AREA END. • You can assume that the dummy node (inserted at the head of the VMA’s list by default) will never be included in the address range specified by mprotect().
• You can assume that fork()/vfork()/cfork()/clone() will not be called in the test-cases for this part of the assignment.
• You are free to create your own helper functions in gemOS/src/mprotect.c
• You have to check the validity of arguments (such as prot, length) passed to mprotect().
• You can assume that, maximum number of VMAs at any point of time will be 128 including the dummy vm area.
Testing
The user space program code is available in gemOS/src/user/init.c. You need to write your test cases in init.c to validate your implementation. The sample test cases in user/testcases/task3 can be copied into init.c to make use of them.
1 Utilities
In order to make things easier, we have given some template functions, structures and a few utility functions that facilitate object creation and deletion. Let’s have look at those functions.
The process control block (PCB) is implemented using a structure named exec context defined in src/include/context.h. One of the important members of exec context for this assignment is the structure vm area.
struct vm area
vm start – The starting address (virtual address) of the vm area mappings. vm end – The ending address (virtual address) of the vm area mappings. access flags – The protection or access flags of the current vm area mappings. vm next – The pointer to the next vm area mappings.
MMAP AREA START & MMAP AREA END
These are constants defined in the file gemOS/src/include/mmap.h which is used to specify the overall start and end limit of the mmap space. All the mappings (vm area) which are created using the mmap syscalls will reside within this limit.
void *os alloc(u32 size)
Allocates a memory region of size bytes. Note that you can not use this function to allocate regions of size greater than 2048 bytes. This function returns 0 in case of error. Example usage: struct vm area *vm = os alloc(sizeof(struct vm area)); void os free(void *ptr to free, u32 size)
Use os free function to free the memory allocated using os alloc. Example usage: os free(vm, sizeof(struct vm area)); int memcpy(char *dest, char *src, u32 size)
This function copies size bytes from src address to dest address.
void *osmap(u64 pfn)
Given a page frame number, it returns a virtual address corresponding the passed page frame number.
u32 os pfn alloc(u32 region)
This function allocates a new frame in the specified region.
To allocate a frame that is to be used to store page table content, use region = OS PT REG.
To allocate a frame that is to be used to store normal data content, use region = USER REG.
u32 os pfn free(u32 region, u64 pfn)
This function frees a page frame given the page frame number and the region to which it belongs to.
s8 get pfn(u32 pfn)
Increments the reference count of a frame by one.
s8 put pfn(u32 pfn)
Decrements the reference count of a frame by one.
s8 get pfn refcount(u32 pfn)
Get reference count of a frame.
struct exec context* get current ctx(void)
This function returns the exec context of the current running process.
Page-Fault Error Code
The error codes generated in case of a page fault are shown in Figure 5, which is taken from course slides. Example interpretation of some of the error codes is as following:
0x4 – User-mode read access to an unmapped page
0x6 – User-mode write access to an unmapped page
0x7 – User mode write access to read-only page
For this assignment, you need to only worry about P, W, U bits.
Figure 5: Page-Fault Error Codes
2 Submission
• Do not have any additional logging/printing in the submitted code.
• You have to include a file named ‘declaration’ in your submission. In the ‘declaration’ file, you have add the following statement:
“I have read the CSE department’s anti-cheating policy available at https://www. cse.iitk.ac.in/pages/AntiCheatingPolicy.html. I understand that plagiarism is a severe offense. I have solved this assignment myself without indulging in any plagiarism. If my submission is found to be plagiarized from the internet, fellow students, etc., then strict action can be taken against me. Your Name and Roll No ”
• In the ‘declaration’ file, you also have to mention the resources, such as websites, open source content you referred to while solving this assignment.
• You have to submit zip file named your roll number.zip (for example: 1211405.zip) containing only the following files in specified folder format:
your_roll_number.zip
|
|—– your_roll_number
|
|—- mprotect.c
|—- declaration
• If your submission is not as per the above instructions, a penalty of 20 marks will be applied on the marks obtained in this assignment.
• Note: No code changes will be allowed after the assignment submission period has ended. So, test your implementation thoroughly with the provided test cases as well as your custom test case
Operating System Memory Management: A Comprehensive Guide
Memory management is a crucial aspect of modern operating systems, ensuring efficient utilization and allocation of system resources. This comprehensive guide delves into the intricate details of memory management concepts and their practical implementation within operating system kernels.
Virtual Memory Mapping and Process Layout
Virtual memory mapping is a fundamental concept in operating systems, allowing processes to access a larger address space than physically available memory. Each process maintains a virtual address space, divided into various segments such as code, data, heap, and stack. The operating system kernel is responsible for managing and mapping these virtual addresses to physical memory frames (Silberschatz et al., 2018).
Page Fault Handling and Lazy Allocation
Page faults occur when a process attempts to access a virtual address that is not currently mapped to physical memory. In modern operating systems, page fault handling is a crucial mechanism that enables lazy allocation of physical memory pages. When a page fault occurs, the kernel can allocate a new physical page frame, update the page table entries, and resume the process execution seamlessly (Tanenbaum and Bos, 2015).
Memory Protection and Access Permissions
Operating systems provide mechanisms to control access permissions for memory regions, ensuring data integrity and security. The mprotect system call allows processes to modify the access permissions (read, write, execute) for specific virtual memory areas. This functionality is essential for implementing memory protection schemes, preventing unauthorized access or modifications (McDougall and Mauro, 2017).
Implementation Considerations
Implementing memory management functionality within an operating system kernel requires careful consideration of various factors, such as:
Virtual Address Space Layout: Defining the appropriate layout for code, data, heap, and stack segments, ensuring optimal utilization and preventing overlaps.
Page Table Management: Efficient data structures and algorithms for managing multi-level page tables, handling page faults, and updating translation entries.
Physical Memory Allocation: Strategies for allocating and deallocating physical memory frames, ensuring fair and efficient resource utilization.
Security and Access Control: Robust mechanisms for enforcing access permissions, preventing unauthorized memory access, and handling potential security vulnerabilities.
Scholarly References:
Silberschatz, A., Galvin, P. B., & Gagne, G. (2018). Operating system concepts (10th ed.). John Wiley & Sons, Inc.
Tanenbaum, A. S., & Bos, H. (2015). Modern operating systems (4th ed.). Pearson Education, Inc.
McDougall, R., & Mauro, J. (2017). Solaris internals: Core kernel architecture (2nd ed.). Prentice-Hall, Inc.
Love, R. (2015). Linux kernel development (3rd ed.). Addison-Wesley Professional.
Gorman, M. (2020). Understanding the Linux virtual memory manager. Prentice-Hall, Inc.