После Segfault: есть ли способ проверить, что указатель все еще действителен?
Я планирую создать механизм регистрации / отслеживания, который пишет адрес (const char*
) строковых литералов в кольцевой буфер. Эти строки находятся в сегменте данных только для чтения и создаются препроцессором с __function__
или же __file__
,
Вопрос: возможно ли проанализировать содержимое кольцевого буфера после Segfault, если все указатели действительны? Под "действительным" я подразумеваю, что они указывают на отображенную область памяти, и разыменование не вызовет ошибку сегментации.
Я работаю с Linux 2.6.3x и GCC 4.4.x.
С наилучшими пожеланиями,
Charly
4 ответа
Конечно, если стек или другая память, на которую вы полагаетесь, повреждена, то могут возникнуть проблемы, но это верно для любого кода.
Предполагая, что нет проблем со стеком или другой памятью, на которую вы полагаетесь, и предполагая, что вы не вызываете никаких функций, таких как malloc()
которые не являются безопасными для асинхронного сигнала, и при условии, что вы не пытаетесь вернуться из обработчика сигнала, тогда не должно быть проблем с чтением или записью буфера из вашего обработчика сигнала.
Если вы пытаетесь проверить, является ли конкретный адрес действительным, вы можете использовать системный вызов, такой как mincore()
и проверьте результат ошибки.
Обычный способ проверить, не приведет ли разыменование области памяти к segfault, - это использовать read()
или же write()
, Например, чтобы проверить, указаны ли первые 128 байтов ptr
безопасно читаемы:
int fd[2];
if (pipe(fd) >= 0) {
if (write(fd[1], ptr, 128) > 0)
/* OK */
else
/* not OK */
close(fd[0]);
close(fd[1]);
}
(write()
вернусь EFAULT
вместо того, чтобы подавать сигнал, если регион не читается).
Если вы хотите проверить больше, чем PIPE_BUF
байт за раз, вам нужно будет читать и отбрасывать со стороны чтения канала.
Я думаю, что вы ищете подход, чтобы справиться с SIGSEGV
сигнал через sigaction
,
void handler(int, siginfo_t *info, ucontext_t *uap)
{
/* Peek at parameters here... I'm not sure exactly what you want to do. */
}
/* Set up the signal handler... */
struct sigaction sa, old_sa;
memset(&sa, 0 sizeof(sa));
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &sa, &old_sa))
{
/* TODO: handle error */
}
Обратите внимание, что ловля SIGSEGV
сам по себе процесс немного странно. Процесс, вероятно, находится в плохом состоянии, из которого невозможно восстановить. Действия, которые вы сможете выполнить в ответ на это, могут быть ограничены, и, скорее всего, убиваемый процесс - это хорошо.
Если вы хотите, чтобы он был немного более стабильным, есть sigaltstack
вызов, который позволяет вам указать буфер альтернативного стека, так что, если вы полностью закрепили свой стек, вы все равно можете обрабатывать SIGSEGV
, Чтобы использовать это вам нужно установить SA_ONSTACK
в sa.sa_flags
выше.
Если вы хотите ответить на SEGV
от безопасности другого процесса (таким образом изолируя себя от плохо функционирующего кода segfaulting и делая его таким образом, чтобы вы не потерпели крах при проверке его), вы можете использовать ptrace
, Этот интерфейс сложен, имеет много непереносимых частей и в основном используется для написания отладчиков. Но вы можете делать с ним большие вещи, такие как чтение и запись памяти и регистров процесса, а также изменять его выполнение.
Как только вы получили segfault, все ставки сняты. Указатели могут быть действительными или они могут быть повреждены. Ты просто не знаешь. Возможно, вы сможете сравнить их с допустимыми значениями или указатель на сам кольцевой буфер может быть поврежден. В этом случае вы, вероятно, получите мусор.