具有mprotect的PROT_READ和PROT_WRITE的行为

Aif*_*Aif 7 c mprotect

我一直试图先使用mprotect反对阅读,然后写作.

这是我的代码

#include <sys/types.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    int pagesize = sysconf(_SC_PAGE_SIZE);
    int *a;
    if (posix_memalign((void**)&a, pagesize, sizeof(int)) != 0)
        perror("memalign");

    *a = 42;
    if (mprotect(a, pagesize, PROT_WRITE) == -1) /* Resp. PROT_READ */
        perror("mprotect");

    printf("a = %d\n", *a);
    *a = 24;
    printf("a = %d\n", *a);
    free (a);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在Linux下这里是结果:

以下是输出PROT_WRITE:

$ ./main 
a = 42
a = 24
Run Code Online (Sandbox Code Playgroud)

并为 PROT_READ

$ ./main 
a = 42
Segmentation fault
Run Code Online (Sandbox Code Playgroud)

在Mac OS X 10.7下:

以下是输出PROT_WRITE:

$ ./main 
a = 42
a = 24
Run Code Online (Sandbox Code Playgroud)

并为 PROT_READ

$ ./main 
[1] 2878 bus error ./main
Run Code Online (Sandbox Code Playgroud)

到目前为止,我知道OSX/Linux的行为可能有所不同,但我不明白为什么PROT_WRITE在读取值时不会使程序崩溃printf.

有人可以解释一下吗?

Ser*_* L. 11

您正在观察两件事:

  1. mprotect不适合与堆页面一起使用.Linux和OS X对堆的处理略有不同(请记住OS X使用Mach VM).OS X不喜欢它的堆页面被篡改.

    如果您通过分配页面,则可以在两个操作系统上获得相同的行为 mmap

    a = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
    if (a == MAP_FAILED) 
        perror("mmap");
    
    Run Code Online (Sandbox Code Playgroud)
  2. 这是MMU的限制(在我的情况下是x86).x86中的MMU不支持可写但不可读的页面.这样设定

    mprotect(a, pagesize, PROT_WRITE)
    
    Run Code Online (Sandbox Code Playgroud)

    什么也没做.而

    mprotect(a, pagesize, PROT_READ)
    
    Run Code Online (Sandbox Code Playgroud)

    删除了写入priveledges并按预期获得SIGSEGV.

此外,虽然它似乎并不成为一个问题在这里,您应该编译代码-O0或设置a,以volatile int *避免任何编译器的优化.

  • `mmap`上的内存来自VM中的空闲区域.它的不同之处在于堆由用户空间`malloc`库(后者调用`mmap`)管理,并允许以字节块分配,VM由内核管理,只允许以块的形式进行分配页面. (2认同)
  • 它不仅仅是x86.我在5-6个不同的cpu架构上使用过MMU,但没有一个读取保护.浪费硅片并不是一个有用的功能,特别是因为您需要一些非常多毛的逻辑来确保通过MMU读取缓存而不允许从缓存中读取,同时还实现逻辑以防止通过MMU进行未缓存的读取.这不值得.缓存标签中的位是昂贵的,PTE中的位是昂贵的,只写内存不是很有用,所以(几乎?)没有人这样做. (2认同)