Как защитить объект - PullRequest
       63

Как защитить объект

0 голосов
/ 02 апреля 2020

Моя проблема

У меня есть синглтон, чья память повреждена неизвестным повреждителем. Что-то перезаписывает память для синглтона и сотни байтов вокруг него со значением 0. После того, как объект сконструирован с помощью new, он доступен только для чтения в течение всего времени жизни приложения.

Моя цель

Я бы хотел поймать коррупционера во время коррупции. Я хотел бы защитить как объект только для чтения память объекта после строительства. Таким образом, позже, когда происходит повреждение, система будет иметь ошибку сегментации во время повреждения.

Мой вопрос

Похоже, что mprotect гранулирован до уровня страницы. Как бы я «перераспределил» для экземпляра-одиночки полную страницу для объекта (она намного меньше, чем 4 КБ, стандартный размер страницы), а затем запустил бы защиту этой страницы?

Ответы [ 3 ]

1 голос
/ 02 апреля 2020

Спасибо, @Brian. Вот мой минимальный пример использования mmap, как он предлагает, с последующим размещением нового для использования этой памяти и затем 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;
}

Когда я компилирую и запускаю это, я получаю cra sh что я желаю:

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.
1 голос
/ 02 апреля 2020

Вы можете использовать анонимный mmap, чтобы выделить полную страницу для синглтона, а затем создать объект в нем с размещением нового.

0 голосов
/ 02 апреля 2020

Практически каждый отладчик получил инструмент для отслеживания изменений памяти (в команде gdb, буквально называемой watch)

Вместо того, чтобы пытаться обойти проблему, вы должны найти источник, повреждение из-за отсутствия связанная запись может касаться чего-то другого, даже более жизненно важного и трудного для обнаружения.

Чтобы ответить на ваш вопрос, в C ++ появилось выражение нового размещения, перегруженное для оператора new, которое позволяет размещать объект по определенному адресу предварительно выделенная память

...