Reputation: 5850
I have a singleton whose memory is being corrupted by an unknown corruptor. Something is overwriting the memory for the singleton, and hundreds of bytes around it, with value 0. After the object is constructed via new, it is read-only for the lifetime of the application.
I'd like to capture the corruptor at the time of the corruption. I'd like to mprotect as read-only the memory of the object after construction. That way later when the corruption happens the system will segmentation fault at the time of corruption.
It looks like mprotect is granular to the page level. How would I "over allocate" for the singleton instance a full page for the object (it is far smaller than 4k, the standard page size) and then mprotect that page?
Upvotes: 0
Views: 767
Reputation: 14613
Practically every debugger got a tool to watch memory for change (in gdb command literally called watch
)
Instead of trying to work around issue, you have to find source, corruption due to out-of-bound write may touch something else, even mory vital and hard to detect.
To answer your question, C++ got a placement new expression, an overload for new operator which allows to allocate object at particular address of pre-allocated memory
Upvotes: 0
Reputation: 5850
Thank you @Brian. Here's my minimal example of using mmap as he suggests, followed by placement new to use that memory and then mprotect to make it read-only:
#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;
}
When I compile and run this, I get the crash that I desire:
g++ -g -Wall -Werror -std=c++17 test.cc -o test
./test
10
runit: line 4: 18029 Bus error: 10 ./test
The debugger points right to the write:
$ 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.
Upvotes: 2
Reputation: 119382
You can use anonymous mmap
to allocate a full page for the singleton, then construct the object into it with placement new.
Upvotes: 2