如何保护对象

How to mprotect an object

我的问题

我有一个单身人士,其内存正被未知的破坏者破坏。某些东西正在覆盖单例的内存,以及它周围的数百个字节,值为 0。通过 new 构造对象后,它在应用程序的生命周期内是只读的。

我的目标

我想在腐败的时候把腐败的人抓起来。我想在构造后将对象的内存 mprotect 为只读。这样以后当损坏发生时,系统将在损坏时出现分段错误。

我的问题

看起来 mprotect 是精细到页面级别的。我将如何 "over allocate" 为单例实例一个完整的对象页面(它远小于 4k,标准页面大小)然后 mprotect 该页面?

您可以使用匿名 mmap 为单例分配一个完整的页面,然后使用新的放置将对象构造到其中。

谢谢@Brian。这是我按照他的建议使用 mmap 的最小示例,然后放置 new 以使用该内存,然后 mprotect 使其成为只读:

#include <iostream>
#include <sys/mman.h>
#include <unistd.h>

using namespace std;

struct MySingleton
{
    int some_value;

    static MySingleton* init(int a_value)
    {
        // Get the system's page size.
        const auto pagesize = getpagesize();
        // mmap one page worth of memory, initially writable.
        void* map = mmap(0, pagesize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0);
        // Use placement new using that memory.
        MySingleton::_instance = new(map) MySingleton(a_value);
        // Now make that memory read-only.
        mprotect(map, pagesize, PROT_READ);
        return MySingleton::_instance;
    }

    static MySingleton* instance()
    {
        return _instance;
    }

private:
    MySingleton(int a_value)
        : some_value{a_value}
    {
    }

    static MySingleton *_instance;
};

MySingleton *MySingleton::_instance = nullptr;


int
main(int argc, char* argv[])
{
    MySingleton *instance = MySingleton::init(10);

    // Read is OK.
    cout << instance->some_value << endl;

    // This should crash;
    instance->some_value = 5;
    cout << instance->some_value << endl;

    return 0;
}

当我编译并运行这个时,我得到了我想要的崩溃:

g++ -g -Wall -Werror -std=c++17 test.cc -o test
./test
10
runit: line 4: 18029 Bus error: 10           ./test

调试器指向写入:

$ lldb test
(lldb) target create "test"
Current executable set to 'test' (x86_64).
(lldb) run
Process 18056 launched: '<snip>' (x86_64)
10
Process 18056 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x10011d000)
    frame #0: 0x0000000100000c50 test`main(argc=1, argv=0x00007ffeefbff9a8) at test.cc:50:26
   47       cout << instance->some_value << endl;
   48   
   49       // This should crash;
-> 50       instance->some_value = 5;
   51       cout << instance->some_value << endl;
   52   
   53       return 0;
Target 0: (test) stopped.

几乎每个调试器都有一个工具来观察内存变化(在 gdb 命令中字面意思是 watch

与其试图解决问题,不如找到源头,由于越界写入而导致的损坏可能会触及其他东西,甚至是非常重要且难以检测的东西。

为了回答你的问题,C++ 得到了一个 placement new 表达式,一个 new 运算符的重载,它允许在预分配内存的特定地址分配对象