C SIGSEGV Handler & Mprotect - PullRequest
       50

C SIGSEGV Handler & Mprotect

4 голосов
/ 24 апреля 2010

Я создаю программу, которая использует mprotect () для ограничения доступа к блоку памяти. Когда запрашивается память, выдается SIGSEGV, который я слушаю, используя вызов signal ().

Как только SIGSEGV обнаружен, мне нужно каким-то образом получить доступ к указателю на запрошенную память (которая вызвала ошибку) и размер запрошенного сегмента. Это возможно?

void fifoSigHandler(){

    // Needs to only remove protection from requested block of virtual memory
    mprotect(fifoVm,(size_t)fifoVm_size,PROT_WRITE);
    printf("Caught Seg Fault");
}

void fifo_init(void* vm, int vm_size, int n_frames, int page_size)
{
    fifoVm = vm;
    fifoVm_size = vm_size;
    fifoFrames = n_frames;
    fifoPageSize = page_size;

    mprotect(fifoVm,(size_t)fifoVm_size,PROT_NONE);

    signal(SIGSEGV, fifoSigHandler);
}

Кроме того, есть ли способ определить уровень mprotect (), которому в данный момент назначен блок памяти (PROT_NONE, PROT_READ и т. Д.)?

Ответы [ 3 ]

6 голосов
/ 24 апреля 2010

Вы должны использовать sigaction с SA_SIGINFO вместо signal, чтобы установить ваш обработчик, а затем вам перезвонят с полезной информацией в siginfo_t, включая si_addr.

si_addr, как объяснено в sigaction (2), будет содержать адрес.Что касается длины, ну, вам не повезло, если вы не готовы разобрать инструкции.Лучшее, что вы можете сделать, - это принять меры для страницы, указанной в si_addr, а затем, если этого недостаточно, вы вскоре получите другой сигнал.По крайней мере, так мы поступили в ObjectStore.

1 голос
/ 29 сентября 2016

Шаг 1 : Инициализация sigaction:

struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
sigemptyset(&act.sa_mask);
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO | SA_ONSTACK;

Шаг 2 : Сделать эту sigaction ручку SIGSEGV:

sigaction(SIGSEGV, &act, NULL);

(Необязательно) Шаг 3 : Заставьте его обрабатывать и другие сигналы памяти:

sigaction(SIGBUS, &act, NULL);
sigaction(SIGTRAP, &act, NULL);

При необходимости добавьте обработку ошибок

Шаг 4 : определение функции обработчика:

void handler(int signal, siginfo_t* siginfo, void* uap) {
    printf("Attempt to access memory at address %p\n", 
           siginfo->si_addr);
    #ifdef LINUX_64BIT
    printf("Instruction pointer: %p\n",
           (((ucontext_t*)uap)->uc_mcontext.gregs[16]));
    #elif LINUX_32BIT
    printf("Instruction pointer: %p\n",
           (((ucontext_t*)uap)->uc_mcontext.gregs[14]));
    #endif
}

Вы можете обратиться к справочным страницам для ucontext_t и siginfo_t для получения более интересных данных, которые может извлечь ваш обработчик.

1 голос
/ 27 сентября 2012

Вы ищете libsigsegv http://libsigsegv.sourceforge.net/

Но учтите, что вызов mprotect безопасен только для сигналов в Linux, другие системы POSIX могут не поддерживать это.

Боюсь, что в Linux единственный способ получить биты защиты памяти - это прочитать в /proc/$pid/meminfo

С другой стороны (только для Linux): если вас беспокоит потребление памяти и вы хотите включить страницы большего сопоставления одну за другой, я бы посоветовал создать ваше сопоставление, используя mmap с MAP_NORESERVE, в этом случае вы получите отображение на заполненные нулями страницы копирования при записи, которые будут выделять физическую память при первой записи. MAP_NORESERVE дает указание ядру не резервировать память с пространством подкачки, что позволяет выделить до 64 ТБ виртуального адресного пространства. Единственным недостатком является то, что если у вас не хватит памяти, могут произойти ужасные вещи (убийца).

...