Обработчик C SIGSEGV и Mprotect
Вопрос
Я создаю программу, которая использует 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 и т.д.)?
Решение
Вы должны использовать sigaction
с участием SA_SIGINFO
вместо signal
установить ваш обработчик, а затем вы получите обратно с полезной информацией в siginfo_t
, включая si_addr
.
si_addr
, как объяснено в sigaction
(2), будет содержать адрес. Что касается длины, ну, вам не повезло, если вы не готовы разбирать инструкции. Лучшее, что вы можете сделать, это принять меры для страницы, представленной в si_addr
, А потом если этого недостаточно, вы получите еще один сигнал достаточно скоро. По крайней мере, вот как мы сделали вещи в ObjectStore.
Другие советы
Вы ищете libsigsegv
http://libsigsegv.sourceforge.net/
Но остерегайтесь этого призвания mprotect
безопасен для передачи сигналов только в Linux, другие системы POSIX могут не поддерживать это.
Я боюсь, что в Linux единственный способ получить биты защиты памяти - это прочитать в /proc/$pid/meminfo
На заметку (только для Linux):Если вы беспокоитесь о потреблении памяти и намерены включать страницы большего отображения одну за другой, я бы посоветовал создать свое отображение с помощью mmap
с MAP_NORESERVE
в этом случае вы получите сопоставление со страницами копирования при записи, заполненными нулем, которые будут выделять физическую оперативную память при первой записи. MAP_NORESERVE
указывает ядру не создавать резервную копию вашей памяти с помощью пространства подкачки, позволяя вам выделить до 64 ТБ виртуального адресного пространства.Единственным недостатком является то, что если у вас действительно закончится память, могут произойти ужасные вещи (оом-убийца).
Шаг 1: Init a. 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
Для более интересных данных ваш обработчик может извлечь.