c - PROT_READ 和 PROT_WRITE 与 mprotect 的行为

标签 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;
}

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 读取值时不会使程序崩溃.

谁能解释一下这部分?

最佳答案

您正在观察两件事:

  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");
    
  2. 这是您的 MMU(在我的例子中是 x86)的限制。 x86 中的 MMU 不支持可写但不可读的页面。从而设置

    mprotect(a, pagesize, PROT_WRITE)
    

    什么都不做。而

    mprotect(a, pagesize, PROT_READ)
    

    删除了写入权限,您将按预期获得 SIGSEGV。

此外,虽然这似乎不是问题,但您应该使用 -O0 编译代码或将 a 设置为 volatile int * 以避免任何编译器优化。

关于c - PROT_READ 和 PROT_WRITE 与 mprotect 的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18829012/

相关文章:

c++ - 多线程应用程序中注入(inject)的 mprotect 调用的切换标志

c - 如何将 CMSIS DSP 库中的转换函数添加到 Makefile 中?

c# - .net/native 编码的 out string 和 StringBuilder 之间有什么区别?

c - 这段代码的循环不变式是什么?

C - 为什么在使用 uint64_t 计数器时 for 循环会卡住,而 while 循环却不会?

linux - 在 Linux 中获取内存权限详细信息

c - 如何编写信号处理程序来捕获 SIGSEGV?

C对内存块的写/读检测

C - 如何检查是否有用户输入