Пытаюсь поймать сигнал SIGSEGV в C

#c #segmentation-fault

Вопрос:

Моя цель состоит в том, чтобы изменить разрешение на зону виртуальной памяти с read-only на read-write . Это должно произойти только после того, как произошел сигнал SIGSEGV.

Я зарегистрировал обработчик сигнала SIGSEGV, а также нашел способ изменить разрешение на read-write .

Текущая проблема, с которой я сталкиваюсь, заключается в том, что обработчик не выполняется.

Есть какие-нибудь идеи?

 #include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <stdlib.h>

void sigint_handler(int signo, siginfo_t *var, void *unused){
        mprotect(var->si_addr, 4, PROT_WRITE);
}

const char str[4] = "abc";

int main(){
        struct sigaction act;

        act.sa_sigaction = sigint_handler;

        sigfillset(amp;act.sa_mask);

        sigaction(SIGSEGV, amp;act, NULL);

        printf("%sn", str);
        //printf("-->%dn",str[-1000]);

        memcpy(str, "123", strlen("123"));

        printf("%sn", str);

        return (0);
}```
 

Комментарии:

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

2. Где срабатывает сигнал?

3. Когда я тестирую под strace, обработчик действительно выполняется, но var->si_addr это -1 так mprotect , что происходит сбой (вы не проверили его возвращаемое значение).

4. Проверьте sigaction справочную страницу. Вы должны установить SA_SIGINFO флаг, если хотите получить siginfo_t аргумент.

5. Обработчик сигнала действительно срабатывает. Вы можете проверить это, запустив под gdb и установив точку останова sigint_handler . Но что ты хочешь сделать? Это особый случай. Хотя обработчик сигналов может установить разрешение на запись (вы хотите: PROT_READ | PROT_WRITE и не только PROT_WRITE ), вам придется заставить программу перезапустить [намеренно] нарушающую инструкцию, что проблематично-приложение не является ядром ОС.

Ответ №1:

Две основные ошибки:

  • При вызове sigaction вы должны установить SA_SIGINFO флаг act.sa_flags , чтобы второй аргумент был передан вашему обработчику сигнала. В нынешнем виде это поле просто неинициализировано.
  • mprotect требует, чтобы его addr аргумент был выровнен по странице. (Это также потребует, чтобы вы пересчитали len аргумент соответствующим образом, чтобы убедиться, что нужный регион охвачен.) В качестве быстрого и грязного примера, предполагая, что размер страницы равен 4096, вы можете замаскировать соответствующие младшие биты, добавив побитовое дополнение 4095.
     uintptr_t orig = (uintptr_t)var->si_addr;
    uintptr_t aligned = orig amp; ~4095UL;
    mprotect((void *)aligned, orig   4 - aligned, PROT_WRITE) < 0);
 

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

С этими исправлениями программа работает так, как она предназначена для меня под Linux.

Комментарии:

1. Не могли бы вы также поделиться кодом? Я добавил флаг, но у меня возникли проблемы с пониманием выравнивания addr .

2. У него есть третья ошибка, из-за которой он не устанавливает структуру sigaction на ноль. Поскольку это локальная переменная, она будет содержать случайный мусор стека, если он не установлен.

3. @ZanLynx: это первая ошибка выше-флаги неинициализированы. При правильной инициализации он указывает, какие другие поля содержат допустимые данные (неиспользуемые поля не нужно обнулять, так как они будут игнорироваться).

4. @JohnnyOnPc: Добавлен пример выравнивания адреса.

5. @ZanLynx: Это правильно задано. Вы не можете безопасно использовать инициализацию структуры, так как порядок полей и наличие или отсутствие заполнения (или объединения для объединения полей) не заданы POSIX. Запоминание всего этого-пустая трата времени, так как вам все равно нужно будет установить все соответствующие поля в любом случае.