#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. Запоминание всего этого-пустая трата времени, так как вам все равно нужно будет установить все соответствующие поля в любом случае.