Главная » Хабрахабр » [Перевод] [The Old New Thing] Могу ли я использовать свой стек как угодно?

[Перевод] [The Old New Thing] Могу ли я использовать свой стек как угодно?

Иногда это определяется архитектурно, а иногда это просто принятое соглашение. В Windows стек растет от больших адресов к меньшим. А значения, расположенные глубже по стеку, соответственно, находятся по большим адресам. Значение указателя стека (регистр процессора), является указателем на значение в верхней части стека. Но что происходит с данными, которые расположены по адресам, меньшим, чем указатель стека?

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

Для Windows размер красной зоны варьируется в зависимости от аппаратной архитектуры и часто равен нулю.

Архитектура

Размер красной зоны

x86

0 байт

x64

0 байт

Itanium

16 байт*

Alpha AXP

0 байт

MIPS32

0 байт

PowerPC

232 байта**

ARM32

8 байт

ARM64

16 байт

* У платформы Itanium стоит отметить особенность: там красная зона расположена над указателем стека, а не под ним.
** В случае PowerPC красная зона является побочным эффектом соглашения о вызовах.

Любая память за красной зоной (ниже по стеку) считается непостоянной (volatile) и может быть изменена операционной системой в любое время.

Я имею в виду, это мой стек! А если серьезно, почему операционную систему вообще заботит то, что я делаю со своим стеком? Что отличает стек от любой другой памяти? Операционная система не говорит мне, что делать с памятью, которую я выделяю через VirtualAlloc.

Рассмотрим следующий код для x86 платформы:

MOV [esp-4], eax ; сохранить eax ниже указателя стека MOV ecx, [esp-4] ; считать сохраненное значение в ecx CMP ecx, eax ; они одинаковые? JNZ panic ; N: случилось что-то безумное

Пояснение к комментарию для инструкции JNZ

В приведенном выше примере инструкция CMP задает вопрос «Являются ли они одинаковыми?». Соглашение о кодировании для ассемблера говорит, что комментарии для инструкций перехода должны описывать результат, если переход выполнен. Таким образом, комментарий начинается с «N:», что означает что переход будет выполнен, если ответом на предыдущий вопрос является «No» (Нет), а в оставшейся части комментария описывается сама ситуация, когда выполняется переход. И инструкция JNZ переходит, если они не равны.

Соглашение о кодировании для ассемблера?

Да, у нас есть соглашение о кодировании для ассемблера.

Возможна ли ситуация, когда условный переход будет осуществлен?

Поэтому для приведенного выше кода возможен переход к метке panic. Поскольку на x86 нет красной зоны, память с отрицательными смещениями относительно указателя стека может быть перезаписана в любое время.

Например, если вы используете команду .call, отладчик выполнит вложенный вызов в том же стеке и, вероятно, использует часть этого пространства стека для сохранения регистров, чтобы их можно было восстановить после возврата из вызываемой функции. Отладчик может использовать память за красной зоной как удобное место для хранения своих данных. Поэтому любые данные, хранящиеся за пределами красной зоны, будут уничтожены.

Вот, например, как это может произойти: Даже во время нормальной работы операционная система может в любой момент перезаписать данные за пределами красной зоны.

Пока ваша нить ожидает возможности возобновить выполнение, менеджер памяти временно забирает у вашего кода физическую страницу памяти (page out). Предположим, что ваша нить (thread) отработала свой квант времени сразу после сохранения данных за красной зоной. О нет, во время подкачки происходит ошибка ввода-вывода! В конце концов ваша нить снова получает управление и менеджер памяти пытается подкачать страницу кода обратно (page in). Операционная система помещает фрейм исключения для STATUS_IN_PAGE_ERROR на стек, что приводит к порче данных, которые вы сохранили за красной зоной.

Она обращается к векторному обработчику исключений (VEH), который является другой частью вашей программы. Операционная система диспетчеризует это исключение. Программа отображает запрос, в котором просит пользователя снова вставить компакт-диск и предлагает повторить попытку. Обработчик был установлен специально, для обработки исключительных ситуаций, возникающих из-за возможного запуска вашей программы непосредственно с CD-ROM или ненадежной сетевой ФС. Если пользователь говорит, что нужно повторить, то обработчик векторных исключений возвращает EXCEPTION_CONTINUE_EXECUTION, а операционная система перезапустит инструкцию, на которой возникло исключение.

Выполняется следующая инструкция, которая загружает значение за пределами красной зоны в регистр ecx. На этот раз перезапуск завершается успешно, потому что CD-ROM присутствует (и читается) и код может быть успешно подкачан в память. Сравнение говорит, что данные разные, и мы переходим к метке panic. Но это уже не то значение, которое было сохранено предыдущей инструкцией, поскольку исключение STATUS_IN_PAGE_ERROR перезаписало его.

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


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Как провалить внедрение CRM-системы?

Одной моей коллеге очень хотелось иметь iPhone 4S. Тогда это был просто верх понта. Получив премию, она отказалась от отпуска и купила его — белый, приятно увесистый, зависть всей коммерческой службы. Через некоторое время она начала жаловаться, что, мол, не ...

Проверка FreeRDP с помощью анализатора PVS-Studio

FreeRDP – свободная реализация клиента Remote Desktop Protocol (RDP), протокола, реализующего удаленное управление компьютером, разработанного компанией Microsoft. Проект поддерживает множество платформ, среди которых Windows, Linux, macOS и даже iOS с Android. Этот проект выбран первым в рамках цикла статей, посвященных ...