Стековый кадр

Сте́ковый кадр (от англ. stack frame) — механизм передачи аргументов и выделения временной памяти (в процедурах языков программирования высокого уровня) с использованием системного стека.

Технология

[править | править код]
Типичный случай использования стека языком высокого уровня на примере вызова процедуры с аргументами «A, B, C» (с соглашениями вызова cdecl) в сравнении с языком ассемблера

Обычно системный стек используется для сохранения адресов возврата при вызове подпрограмм, а также сохранения/восстановления значений регистров процессора.

Передача аргументов

[править | править код]

При вызове процедуры аргументы отправляются в стек, и только потом производится вызов подпрограммы. Таким образом, процедура получает стек, на вершине которого лежит адрес возврата, а под ним — аргументы, с которыми она была вызвана.

При возвращении из процедуры (или после него, см. ниже) аргументы должны быть сняты со стека.

Выделение временной памяти

[править | править код]

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

Перед возвратом процедура должна вернуть указатель стека в оригинальное положение (то есть на адрес возврата).

Соглашения для разных языков программирования

[править | править код]

Различные компиляторы языков высокого уровня по-разному подходят к организации стекового кадра в зависимости от особенностей аппаратной платформы и стандартов конкретного языка. Основные отличия касаются порядка передачи аргументов в стек и их снятия со стека при возврате.

Недостатки стекового кадра

[править | править код]

Стековый кадр — удобная технология выделения временной памяти для передачи произвольного числа аргументов или внутреннего использования. Однако она имеет ряд недостатков.

Производительность

[править | править код]

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

Для уменьшения обращений к локальным переменным программа оптимизируется при компиляции для использования регистров вместо переменных в памяти или для хранения их промежуточных значений.

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

Безопасность

[править | править код]

Стековый кадр перемещает данные приложения с критическими данными — указателями, значениями регистров и адресами возврата. Это, в сочетании с архитектурными особенностями некоторых процессоров (а именно — направлением роста стека), делает злонамеренное перекрытие критических данных в результате переполнения буфера очень легко достижимым (разумеется, прежде всего, программа должна содержать ошибку, которая позволит выполнить переполнение).

Такое «неудачное», с точки зрения переполнения буфера, направление роста машинного стека имеют аппаратные платформы: X86.

Атака по переполнению буфера в стеке обычно реализуется следующим образом:

  • Атакующая программа отправляет на сетевое соединение (или другое средство межпроцессного взаимодействия) блок данных, заведомо больший размера буфера целевой программы.
  • Неправильно написанная (уязвимая) процедура позволяет лишним данным быть записанными дальше границ буфера (то есть допускает его переполнение), через начало стекового кадра и адрес возврата. Блок данных скомпонован так, чтобы адрес возврата в стеке замещался адресом кода эксплойта, который находится в том же загруженном блоке данных (выполнение этого кода с привилегиями выполняемой программы и есть цель атаки).
  • При возврате из уязвимой процедуры управление через изменённый адрес возврата передается на код эксплойта.