Memory page permissions define how a program can interact with specific regions of memory at runtime. These permissions are enforced by the operating system through the Memory Management Unit (MMU), ensuring that each memory page adheres to the access restrictions defined during memory allocation or program loading.
Common Memory Permissions
Each memory page can be assigned one or more of the following permissions:
- Read (
PROT_READ): The page can be read. - Write (
PROT_WRITE): The page can be modified. - Execute (
PROT_EXEC): The page can execute machine code. - No Access (
PROT_NONE): The page cannot be accessed.
These permissions can be combined (e.g., PROT_READ | PROT_EXEC) to control how memory regions behave. For example, these are the usedm permissions in those scenarios:
- .text Section (see Executable Binary Formats): Typically marked as
PROT_READ | PROT_EXECto allow execution of program instructions while preventing modifications. - .data Section (see Executable Binary Formats): Marked as
PROT_READ | PROT_WRITEto enable modification of global and static variables. - Heap/Stack: Often marked as
PROT_READ | PROT_WRITEfor dynamic data storage, but not executable to mitigate security risks (e.g., preventing code execution on the stack).
Setting Memory Permissions
Memory permissions are controlled using system calls. Commonly used calls include:
mprotect
The mprotect syscall changes the protection on a region of memory, so this program will cause a segmentation fault
#include <sys/mman.h>
#include <unistd.h>
int main() {
// Allocate memory (4KB page)
void *addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// Change permissions to read-only
mprotect(addr, 4096, PROT_READ);
// Attempt to write (will cause a segmentation fault)
*(char *)addr = 'A';
return 0;
}mmap
The mmap syscall maps files or anonymous memory into a process’s address space with specified permissions, in this case a file is mapped in read-only okmode.
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("example.txt", O_RDONLY);
void *addr = mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, fd, 0);
// Access memory as read-only
char data = *((char *)addr);
munmap(addr, 4096);
close(fd);
return 0;
}Real-World Applications
- Executable Memory: Ensuring
.textsections are not writable prevents runtime code modification, mitigating exploits like buffer overflow attacks. - Dynamic Loading: Libraries loaded at runtime often require
PROT_READ | PROT_EXECpermissions. - Security Enhancements:
- Data Execution Prevention (DEP): Prevents execution of writable pages by disallowing
PROT_WRITE | PROT_EXECcombinations. - Address Space Layout Randomization (ASLR): Randomizes memory layouts to make exploits harder.
- Data Execution Prevention (DEP): Prevents execution of writable pages by disallowing