0%

mprotect 内存保护

NAME
       mprotect — set protection of memory mapping

SYNOPSIS
       #include <sys/mman.h>

       int mprotect(void *addr, size_t len, int prot);

mprotect 将从 addr 开始,长度为 len 的内存区修改为 prot 指定的保护属性

其中, addr 必须是一个内存页的起始地址, len 必须是页的整数倍

prot 可以取值

  • PROT_READ, 内存中内容可读
  • PROT_WRITE,内存中内容可写
  • PROT_EXEC,内存中内容可执行
  • PROT_NONE,内存中内容无法访问

如果调用进程内存访问行为侵犯了这些设置的保护属性,内核会为该进程产生 SIGSEGV

guard page

posix threads

While using posix threads in Linux it is done automatically with the guard size attribute that is one page by default

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/mman.h>

void *threadfn(void *p)
{
    int val=100;
    printf("thread val address=%lx\n",&val);
    sleep(100);
    return NULL;
}

int main()
{
    int val=100;
    printf("main val address=%lx\n",&val);
    pthread_t t1,t2,t3;
    pthread_create(&t1,NULL,threadfn,NULL);
    pthread_create(&t2,NULL,threadfn,NULL);
    pthread_create(&t3,NULL,threadfn,NULL);
    sleep(100);
    pthread_exit(NULL);
    return 0;
}

/* Output:
main val address=7ffe2438515c
thread val address=7fdba3989f44
thread val address=7fdba3a8af44
thread val address=7fdba4274f44
*/

文件 /proc/[pid]/maps

2eaaa000-2eaab000 ---p 2eaaa000 00:00 0     //one page(4k) guard page
2eaab000-2ebaa000 rw-p 2eaab000 00:00 0
2ebaa000-2ebab000 ---p 2ebaa000 00:00 0     //one page(4k) guard page
2ebab000-2ecaa000 rw-p 2ebab000 00:00 0
2ecaa000-2ecab000 ---p 2ecaa000 00:00 0     //one page(4k) guard page
2ecab000-2edaa000 rw-p 2ecab000 00:00 0
2edaa000-2edab000 ---p 2edaa000 00:00 0     //one page(4k) guard page
2edab000-2eeaa000 rw-p 2edab000 00:00 0
2eeaa000-2eeab000 ---p 2eeaa000 00:00 0     //one page(4k) guard page
2eeab000-2efaa000 rw-p 2eeab000 00:00 0
2efaa000-2efab000 ---p 2efaa000 00:00 0     //one page(4k) guard page

posix threads 提供一组接口

NAME
       pthread_attr_setguardsize, pthread_attr_getguardsize - set/get guard size attribute in thread attributes object

SYNOPSIS
       #include <pthread.h>

       int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
       int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);

       Compile and link with -pthread.
NOTES
       A guard area consists of virtual memory pages that are protected to prevent read and write  access.   If  a  thread
       overflows its stack into the guard area, then, on most hard architectures, it receives a SIGSEGV signal, thus noti‐
       fying it of the overflow.

stack overflow 时会触发 SIGSEGV

others memory

But what about memory that is not located in the stack like dynamic allocation or buffers in BSS/DATA sections? here you can use mprotect to create that guard area

mprotect can be used to change MMU permissions on any mapped memory

One point to notice is that the buffer must be aligned to a page boundary so we can’t allocate it using malloc. we can use memalign or aligned_alloc but the best way in this case is to call mmap directly and not use any heap. mmap returns page(s) and if we use it with MAP_ANONYMOUS we get memory from the kernel

p1=mmap(0,0x20000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
mprotect(p1+page_size,page_size,PROT_NONE);
mprotect(p1+0x1f000,0x1000,PROT_NONE);
pthread_attr_setstack(&attr,p1+0x1000,0x1e000);
pthread_create(&t1,&attr,threadfn1,NULL);

Ref

  1. Using mprotect system call to debug memory problems
  2. mprotect on heap aligned memory works mysteriously
  3. linux c 之使用 mprotect 检测内存访问