c++ - 试图用 mmap 引起段错误

标签 c++ linux segmentation-fault mmap

只是为了了解它是如何工作的,我尝试使用 mmap 从内核分配一些内存,然后设置保护位,这样任何内存访问都会导致段错误,之后我想尝试将保护位设置为不会再次发生段错误。

对 mprotect 的调用失败,并且 si_addr 中的地址是错误的,即使 sigaction 的 linux 手册页说 siginfo 结构的si_addr 函数包含导致错误的地址。并且该地址不是 main() 函数中分配的地址。 代码在 mac 上运行良好

#define _XOPEN_SOURCE

#include <iostream>
#include <signal.h>
#include <ucontext.h>
#include <sys/mman.h>
#include <string.h>
#include <cstdlib>

using std::cout;
using std::cerr;
using std::endl;

void handle_signal(int signal_number, siginfo_t* signal_info, void* context);
void register_signal_handler();

int counter = 0;

int main() {
    register_signal_handler();
    int* page_mapped = (int*) mmap(nullptr, 100, PROT_NONE,
            MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    if (page_mapped == MAP_FAILED) {
        cerr << "mmap failed" << endl;
    }
    cout << "page mapped is " << reinterpret_cast<uintptr_t>(page_mapped)
         << endl;

    // cause the segmentation fault
    cout << *page_mapped << endl;

    return 0;
}

void handle_signal(int, siginfo_t* siginfo, void*) {
    cout << "Handled a segmentation fault" << endl;
    cout << "The segmentation fault was caused by the address "
         << reinterpret_cast<uintptr_t>(siginfo->si_addr) << endl;
    if (mprotect(siginfo->si_addr, 100, PROT_READ | PROT_WRITE) == -1) {
        cerr << "mprotect failed" << endl;
        exit(1);
    }

    // stop an infinite loop
    ++counter;
    if (counter == 3) {
        cerr << "Counter got to 3, probably going into an infinite loop.. "
            "stopping" << endl;
        exit(1);
    }
}

void register_signal_handler() {
    struct sigaction sigaction_information;
    memset(&sigaction_information, 0, sizeof(struct sigaction));
    sigaction_information.sa_sigaction = &handle_signal;
    sigaction(SIGSEGV, &sigaction_information, nullptr);
}

最佳答案

参见 this answer .它解释了 SIGSEGV信号处理程序应该改变机器状态,否则重新启动相同的机器指令并给出一些异常,内核将其转换为发送的相同信号(在相同的“上下文”中),因此循环。

顺便说一句,在信号处理程序中使用 C++ I/O(甚至是 <stdio.h> )是错误的(因为您正在使用非异步信号安全函数)。仔细阅读signal(7) .请注意,禁止信号处理程序调用许多函数(那些不是异步信号安全的函数)。

然后您调用 mprotect(2)是错误的(并且失败了)。大小应该是页面大小的倍数(通常为 4K),地址也应该是页面大小的倍数(您可能应该使用 page_mapped 而不是 siginfo->si_addr 作为 mprotect 的地址参数;或者您可以向下 siginfo->si_addr 舍入到 4K 页面大小的前一个倍数)。当我运行你的程序时(由 g++ -O -Wall curious.cc -o curious 在 Debian/x86-64 上使用 GCC 6 和 linux 内核 4.8 编译)它提示:mprotect failed (有 EINVAL 错误,由 perror(3) 给出)。

你可以使用 strace(1)更准确地了解正在发生的事情。

最后,你的 counter应声明为 volatile .

通过同时声明 counterpage_mapped作为 volatile 全局变量:

volatile int counter;
int*volatile page_mapped;

并通过内部 handle_signal以下代码(在我的系统上,页面大小为 4K):

if (mprotect(page_mapped, 4096, PROT_READ | PROT_WRITE) == -1) {
  /// this is still wrong in theory, 
  /// .... since we are using non-async signal safe functions
  perror("mprotect");
  exit(EXIT_FAILURE);
  /// but in practice mprotect is successful
}

它的行为不同(并且更像你希望的那样)因为 mprotect没有失败,最终值为 counter (在 main 末尾)为 1(如您所愿)。

关于c++ - 试图用 mmap 引起段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40956319/

相关文章:

c - C程序运行时出现错误

c - 将字符存储在数组段错误中

mysql pam插件错误

python - wx 应用程序中的 Matplotlib 绘图时出现段错误

C - 出现段错误(核心已转储)

c++ - gcc 与 clang、msvc 和 icc : Is this function call ambiguous?

c++ - 如何将 vector 大小与整数进行比较?

c++ - 不使用OpenCV函数在C++中旋转图像

c++ - 循环套接字连接尝试最终会断开互联网连接

linux - 如何读取 iw wlan0 站输出