如何保护对象
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 运算符的重载,它允许在预分配内存的特定地址分配对象
我的问题
我有一个单身人士,其内存正被未知的破坏者破坏。某些东西正在覆盖单例的内存,以及它周围的数百个字节,值为 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 运算符的重载,它允许在预分配内存的特定地址分配对象