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_EXEC to allow execution of program instructions while preventing modifications.
  • .data Section (see Executable Binary Formats): Marked as PROT_READ | PROT_WRITE to enable modification of global and static variables.
  • Heap/Stack: Often marked as PROT_READ | PROT_WRITE for 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

  1. Executable Memory: Ensuring .text sections are not writable prevents runtime code modification, mitigating exploits like buffer overflow attacks.
  2. Dynamic Loading: Libraries loaded at runtime often require PROT_READ | PROT_EXEC permissions.
  3. Security Enhancements:
    • Data Execution Prevention (DEP): Prevents execution of writable pages by disallowing PROT_WRITE | PROT_EXEC combinations.
    • Address Space Layout Randomization (ASLR): Randomizes memory layouts to make exploits harder.