具有mprotect的PROT_READ和PROT_WRITE的行为
我一直在尝试使用mprotect
首先阅读,然后写作。
这是我的代码
#include #include #include #include #include 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; }
在Linux下这里是结果:
这是PROT_WRITE
的输出:
$ ./main a = 42 a = 24
并为PROT_READ
$ ./main a = 42 Segmentation fault
在Mac OS X 10.7下:
这是PROT_WRITE
的输出:
$ ./main a = 42 a = 24
并为PROT_READ
$ ./main [1] 2878 bus error ./main
到目前为止,我知道OSX / Linux的行为可能有所不同,但我不明白为什么PROT_WRITE
在使用printf
读取值时不会使程序崩溃。
有人可以解释这部分吗?
您正在观察两件事:
-
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");
-
这是MMU的限制(在我的情况下是x86)。 x86中的MMU不支持可写但不可读的页面。 这样设定
mprotect(a, pagesize, PROT_WRITE)
什么也没做。 而
mprotect(a, pagesize, PROT_READ)
删除了写入priveledges并按预期获得SIGSEGV。
此外,虽然这似乎不是问题,但您应该使用-O0
编译代码或将a
设置为volatile int *
以避免任何编译器优化。
大多数操作系统和/或cpu体系结构在可写时会自动生成可读的内容,因此PROT_WRITE
通常也意味着PROT_READ
。 如果不使其可读,就不可能制作可写的东西。 可以推测原因,或者不值得在MMU和缓存中创建额外的可读性位,或者在某些早期架构中,您实际上需要在写入之前将MMU读入缓存,所以自动制作不可读的内容会使其无法写入。
此外, printf
可能会尝试从内存中分配您使用mprotect
损坏的mprotect
。 当您更改其保护时,您希望从libc分配整页,否则您将更改您不完全拥有的页面的保护,并且libc不希望它受到保护。 在使用PROT_READ
MacOS测试时,会发生这种情况。 printf
分配一些内部结构,尝试访问它们并在只读时崩溃。