Есть ли способ гарантировать сегфо? - PullRequest
0 голосов
/ 22 февраля 2019

Я знаю, что сегфо является распространенным проявлением неопределенного поведения.Но у меня есть два небольших вопроса по этому поводу:

  1. Является ли ВСЕ segfaults неопределенным поведением?

  2. Если нет, есть ли способ убедиться в том, что segfault?

Что такое ошибка сегментации? гораздо более общий, чем мой вопрос, и ни один из ответов не отвечает ни на один из моих вопросов.

Ответы [ 4 ]

0 голосов
/ 22 февраля 2019

Предполагая, что платформа вообще поддерживает segfaults, вот несколько возможностей:

  • Использование raise.Это приведет к заметному изменению siginfo_t, но часто это не имеет значения.
  • Разыменование энергонезависимого указателя.Скорее всего, это будет оптимизировано как «недостижимое».
  • Разыменуйте изменчивый указатель.Это должно помешать компилятору оптимизировать доступ.
  • Используйте asm volatile и разыменуйте указатель.Обязательно включите фальшивый вывод, который будет использоваться позже, иначе компилятор все еще может выполнять нежелательные оптимизации.Для этого требуется специальный код для каждой платформы.
  • Сборка модуля ядра, который напрямую создает SIGSEGV с соответствующим siginfo_t.Это предполагает, что модули ядра даже могут быть загружены (или даже скомпилированы).

Если мы разыменовываем указатель, можем ли мы:

  • readчерез него, или
  • записать через него, или
  • оба?

и какой указатель это должен быть?

  • NULL - популярный выбор, но иногда возможно отображение этой страницы.В наши дни ядра, обеспечивающие безопасность, обычно запрещают это.
  • Используйте действительный, но не выровненный указатель.Это сильно зависит от платформы, и, вероятно, производит другое siginfo_t.Может быть, даже SIGBUS или что-то в этом роде.
  • Используйте неправильно выровненный указатель, который охватывает страницы, из которых одна страница не отображена или защищена, как показано ниже.
  • вызовите mmap с защитой, которая запрещает этодоступ вы пытаетесь.Я не помню, различим ли это siginfo_t или нет.Может гоняться, но только если какой-то другой поток в программе враждебен.Для mmap также возможен сбой.
  • вызов mmap, за которым сразу следует munmap, затем разыменуйте указатель.В однопоточных программах с заблокированными сигналами это может привести к адресу, который гарантированно не будет отображен.Тем не менее, он может работать в многопоточных программах, и может произойти сбой начального mmap.
  • вызов mmap в цикле, пока не произойдет сбой, потому что вы исчерпали ограничение ядра в 64 КБ дляколичество отображенных областей памяти.Затем выполните синтаксический анализ / proc / self / maps и найдите адрес, на который не распространяется ни один из них.Это может быть неестественно, если другие потоки в настоящее время munmap ing.Если другие потоки в настоящее время mmap работают, они могут загадочно завершиться сбоем, прежде чем ваш segfault убьет процесс.

В итоге, совершенно надежного / переносимого метода не существует,но есть много достаточно хороших.

0 голосов
/ 22 февраля 2019

Вы никогда не ошибетесь с разыменованием указателя NULL.

int main() {
  int *a = 0;
  *a = 0;
  return 0;
}

Как справедливо отмечают комментарии, это не будет работать 100% времени и зависит от платформы.Но это должно работать на большинстве распространенных платформ.

0 голосов
/ 22 февраля 2019
  1. Являются ли ВСЕ segfaults неопределенным поведением?

Этот вопрос сложнее, чем может показаться, потому что "неопределенное поведение" является описанием любой исходной программы на Cили результат запуска C-программы на «абстрактной машине», которая описывает поведение C-программ в целом;но «ошибка сегментации» - это возможное поведение конкретной операционной системы, часто с помощью определенных функций ЦП.

Стандарт C ничего не говорит о сбоях сегментации.Одна почти уместная вещь, которую он говорит, это то, что если выполнение программы не имеет неопределенного поведения, то выполнение программы в реальной реализации будет иметь такое же наблюдаемое поведение, как и выполнение абстрактной машины.А «наблюдаемое поведение» определено так, чтобы включать в себя только доступ к изменчивым объектам, данные, записанные в файлы, а также ввод и вывод интерактивных устройств.

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

Таким образом, ситуация, когда программа вызывает ошибку сегментации (для ОС), хотя нет неопределенного поведения (согласно стандарту C) не имеет особого смысла для реального компилятора и ОС, но мы не можем полностью исключить это.

Но также и все, что предполагает идеальные компьютеры.Если оперативная память плохая, предполагаемое значение адреса может измениться.Есть даже очень редкие, но измеримые события, когда космические лучи могут немного измениться в остальном хорошей оперативной памяти.Мягкие ошибки, подобные этим, могут вызвать ошибку сегментации (в системе, где «ошибка сегментации» - вещь), практически для любой прекрасно написанной программы на С, без какого-либо неопределенного поведения, возможного для любой реализации или ввода.

Если нет, то есть ли способ обеспечить защиту от ошибок?

Это зависит от контекста и того, что вы подразумеваете под "обеспечить".

Можете ли вынаписать программу на C, которая всегда вызывает segfault?Нет, потому что некоторые компьютеры могут даже не иметь такой концепции.

Можете ли вы написать программу на C, которая всегда вызывает segfault, если это возможно на компьютере?Нет, потому что некоторые компиляторы могут делать что-то, чтобы избежать реальной проблемы в некоторых случаях.А поскольку поведение программы не определено, то, что не вызывает segfault, является таким же допустимым результатом, как и причина segfault.В частности, одно реальное препятствие, с которым вы можете столкнуться, выполняя даже такие простые вещи, как намеренное разыменование значения нулевого указателя, заключается в том, что при оптимизации компилятора иногда предполагается, что входные данные и логика всегда будут работать так, что неопределенное поведение не произойдет, так как это нормальноне делайте то, что программа говорит для входов, которые приводят к неопределенному поведению.

Зная подробности о том, как одна конкретная ОС, и, возможно, ЦП, обрабатывает память и иногда генерирует ошибки сегментации, вы можете написать инструкции по сборке, которые всегда будутвызвать segfault?Конечно, если обработка segfault имеет какое-либо значение вообще.Можете ли вы написать программу на C, которая будет вызывать segfault примерно таким же образом?Скорее всего.

0 голосов
/ 22 февраля 2019

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

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

  2. Самый простой способ - использовать функцию raise.

Источник:

#include <signal.h>    
int main() {
    raise(SIGSEGV);
    return 0;
}
...