Когда ваш обработчик сигнала возвращается (при условии, что он не вызывает exit или longjmp или что-то, что мешает его фактическому возвращению), код продолжит работу в том месте, где произошел сигнал, повторно выполнив ту же инструкцию. Поскольку в этот момент защита памяти не изменилась, он просто снова сгенерирует сигнал, и вы вернетесь в обработчик сигнала в бесконечном цикле.
Итак, чтобы заставить его работать, вы должны вызвать mprotect в обработчике сигналов. К сожалению, как отмечает Стивен Шенскер, mprotect не является асинхронно безопасным, поэтому вы не можете безопасно вызывать его из обработчика сигнала. Итак, что касается POSIX, вы облажались.
К счастью, в большинстве реализаций (насколько мне известно, во всех современных вариантах UNIX и Linux) mprotect является системным вызовом, поэтому безопасно вызывать из обработчика сигнала , поэтому вы можете выполнять большинство то, что ты хочешь. Проблема в том, что если вы хотите изменить защиту обратно после чтения, вам нужно будет сделать это в основной программе после чтения.
Другая возможность состоит в том, чтобы сделать что-то с третьим аргументом обработчику сигнала, который указывает на специфическую структуру ОС и арки, которая содержит информацию о том, где произошел сигнал. В Linux это структура ucontext , которая содержит машинную информацию об адресе $ PC и другое содержимое регистра, где произошел сигнал. Если вы измените это, вы измените, куда обработчик сигнала будет возвращаться, так что вы можете изменить $ PC, чтобы он был сразу после ошибочной инструкции, чтобы он не выполнялся повторно после возврата обработчика. Это очень сложно сделать правильно (и непереносимо).
редактировать
Структура ucontext
определена в <ucontext.h>
. В ucontext
поле uc_mcontext
содержит машинный контекст, а в , что , массив gregs
содержит общий регистровый контекст. Итак, в вашем обработчике сигналов:
ucontext *u = (ucontext *)unused;
unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_RIP];
даст вам компьютер, на котором произошло исключение. Вы можете прочитать это, чтобы выяснить, какая инструкция это
это было ошибкой, и делай что-то другое.
Что касается переносимости вызова mprotect в обработчике сигналов, любая система, которая соответствует либо спецификации SVID, либо спецификации BSD4, должна быть безопасной - они позволяют вызывать любой системный вызов (что угодно в разделе 2 руководства). в обработчике сигнала.