Как именно инициируется исключение нарушения прав доступа - PullRequest
7 голосов
/ 30 января 2011

Иногда ошибки могут вызывать исключение нарушения доступа к памяти.

Как именно это исключение вызывается?Какой механизм работает за кулисами?

Нужна ли ему поддержка от ЦП (начиная с какого ЦП?) / От ОС (начиная с какой версии?) / От компилятора (начиная с какой версии?)

Редактировать:

Один конкретный сценарий, который я хочу лучше понять:

Следующий код может вызвать исключение нарушения доступа.

TCHAR* czXXX= _T("ABCDEFG");
czXXX[0]= 'A';

Полагаю, czXXX указывает на блок памяти только для чтения, но что именно происходит?

Ответы [ 6 ]

11 голосов
/ 30 января 2011

Нарушения доступа к памяти являются большой темой:)

Защита информации в компьютерных системах (с 1973 :) лежит в основе механизма сегментов ,где процессам выделены base и bound ;любая попытка получить доступ к памяти за пределами диапазона base:base+bound означала, что программа сделала что-то глупое и должна быть убита.

Линейка процессоров 80x86 обеспечивает базовую поддержку сегментов и ядро ​​безопасности GEMSOS это ядро ​​операционной системы, сертифицированное A1, основанное на этом механизме.

Но сегменты не очень динамичны, и почти все современные операционные системы - это системы подкачки , то есть страница в памяти, когда она недоступна.Это основывается на ЦП, имеющем MMU , блок управления памятью, который проверяет все обращения к памяти на предмет правильных привилегий и наличия / отсутствия правильного отображения памяти.Когда процесс пытается получить доступ к памяти, которая в данный момент не отображается в ОЗУ, MMU сообщает ЦПУ о том, что произошла ошибка, и ЦП приостанавливает процесс для загрузки запрошенной страницы памяти с диска.(Или, если память не должна отображаться для процесса, скажем, он пытается получить доступ к 0x0 или к какой-либо произвольной ячейке памяти, которая не была сопоставлена ​​с mmap или подобными примитивами, выделяющими память, это убивает процесс.)

Intel 80386 был первым чипом Intel с поддержкой подкачки страниц, поэтому «386 Enchanced Mode» в Windows 3.1 был , поэтому намного лучше, чем режим 286.

Компиляторына самом деле не задействованы, но процессор, MMU и ядро ​​операционной системы должны все работать вместе.

6 голосов
/ 30 января 2011

В архитектуре x86 (и в большинстве других) она запускается из MMU - модуля управления памятью. MMU используется для преобразования адресов виртуальной памяти в адреса физической памяти. Если сделан запрос на доступ к недопустимому адресу (0x00000000 или что-то слишком высокое), MMU будет перехватывать (прерывать) ОС (это фактически делается для каждого доступа, не входящего в кэш TLB (Translate Lookaside Buffer - Translation of MMU) ». «)). Здесь ОС сможет сказать, что это незаконный доступ к памяти, и распространяться на пользовательское приложение через механизм, зависящий от ОС (сигналы в Linux (SIGSEGV). Я недостаточно знаком с Windows, чтобы сказать, как это делается. в нем).

Эта функция доступна для любого современного процессора, ОС и компилятора. Самым основным требованием является MMU, который присутствует во всех, кроме самых основных встроенных процессоров. Я сомневаюсь, что есть какой-либо ПК, который в настоящее время работает, который не поддерживает это.

Edit:

После редактирования OP, когда используется литеральная строка, память помещается в сегмент .text исполняемого файла. Это где двоичный код и постоянные значения. Естественно, в большинстве ОС это доступно только для чтения (особенно в Linux с различными улучшениями безопасности). Когда вы пытаетесь изменить значение литеральной строки, вы в основном пытаетесь записать в постоянную память, что вызывает нарушение прав доступа. Опять же, это отлавливается MMU, который видит команду записи в адрес памяти только для чтения.

2 голосов
/ 30 января 2011

Все эти ответы очень хорошо объясняют, что происходит на уровне процессора. Как уже упоминалось, как только процессор выдает прерывание на ОС, ситуация меняется в зависимости от операционной системы.

Windows использует механизм, известный как « Структурированные исключения ». Очень важно не путать это с исключениями C ++, они разные. Структурные исключения концептуально работают так же, как исключения C ++, в том, что они разматывают стек в поисках обработчика. Поскольку структурированные исключения не зависят от языка, они не вызывают деструкторы или выполняют какую-либо очистку.

Структурированные исключения могут быть перехвачены с помощью

__try
{
  //Usual code
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
  //Handling code
}
__finally
{
  //Cleanup
}

Имейте в виду, однако, что как только вы получите структурированное исключение, в вашей программе уже произошел сбой, , поэтому нет нормального способа «восстановить».

Подробнее о SEH .

2 голосов
/ 30 января 2011

Когда вы пытаетесь получить доступ к адресу памяти, компьютер выполняет несколько шагов:

  • Если адрес является частью текущего сегмента памяти, доступ предоставляется.
  • В противном случае, если сегмент адреса находится в памяти с соответствующими правами доступа , доступ предоставляется.

Если адрес отсутствует в памяти, ЦП сгенерирует исключение проверки памяти. На этом этапе операционная система вступает во владение.

  • Если сегмент доступен в виртуальной памяти с соответствующими правами доступа , он загружается в память и назначается менеджеру виртуальной памяти; доступ предоставляется.

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

Что касается версий CPU и OS, это любая система, которая допускает виртуальную память. Я не знаю деталей этого.

1 голос
/ 30 января 2011

Нарушение доступа к памяти может возникнуть и здесь:

delete pSample;

//again deleting the same memory!
delete pSample;

В таких случаях возникает исключение из модели памяти ОС. Главным образом это ОС, чья работа заключается в проверке доступа к памяти из любого процесса!

0 голосов
/ 30 января 2011
void Kaboom()
{
    int* certain_death = NULL;
    *certain_death = 0;
}
...