Архитектура памяти стека вызовов - PullRequest
2 голосов
/ 15 января 2010

После моего вчерашнего вопроса я попытался узнать немного больше об архитектуре стеков вызовов. Поиски в Интернете и ТАК не дали нужного мне ответа, возможно, потому что я не знаю точно, какие ключевые слова использовать. Во всяком случае, я уверен, что кто-то здесь может мне помочь ...

Сначала давайте начнем с выдержки из записи Википедии о переполнении буфера в стеке :

В программном обеспечении переполнение буфера стека происходит, когда программа записывает адрес памяти в стеке вызовов программы за пределами предполагаемой структуры данных; обычно буфер фиксированной длины.

Коллега сказал мне, что он вспомнил, что узнал, что в Linux стек находится в самом конце «виртуальной памяти» процесса и при необходимости увеличивается назад - следовательно, это не будет «буфер фиксированной длины». Однако я не смог подтвердить это. Итак, мои вопросы:

  1. На Windows и Linux это вызов стек всегда фиксированный размер буфера? Если нет, как это растет? Как это управлять разделением виртуальной памяти с кучей?
  2. Архитектура стека зависит от используемый язык? На ОПЕРАЦИОННЫЕ СИСТЕМЫ? На железе?
  3. это размер стека определяется в время компиляции или его можно изменить апостериорная
  4. Как и где стеки вызовов отдельных потоков выделяется

Ответы [ 2 ]

3 голосов
/ 15 января 2010

переполнение буфера стека

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

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

| start of stack |
| data           |
| parameters     |
| return address |
| data           |
| parameters     |
| return address |
| parameters     |
| return address | <- might overflow into this region or above
| string data    | <- writes to this region ... (look up)
 stack head
  |
  V direction of growth for pushes
 ...
| end of stack   |
| guard page     | <- writes to this region cause a segfault
 ...
| heap           |

Стек вызовов в Linux

Стек вызовов имеет фиксированный размер, сам стек увеличивается и уменьшается в соответствии с требованиями этого предела.

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

Размер основного стека процесса определяется средой, существующей в момент запуска программы. Для функций c, чтобы установить это, смотрите man 3 ulimit, а чтобы просмотреть / установить его из bash, смотрите, ulimit -s для деталей.

> ulimit -s
8192

Если вы создаете свои собственные потоки, вы можете взять на себя ответственность за создание их стеков (см. man pthread_attr), вы можете либо использовать рекомендуемый системой размер, либо установить свой собственный.

2 голосов
/ 15 января 2010

Для Windows:

  1. Для приложения пользовательского режима по умолчанию память для стека изначально резервируется на 1 МБ. Зарезервированный означает, что диапазон адресов не может быть использован для каких-либо других целей, но память фактически не выделена. Это позволяет стеку быть непрерывным в памяти, но не требует, чтобы все его части (даже если большинство из них не использовались) были выделены по умолчанию. В конце фактического выделенного стека появляется страница защиты - каждый раз, когда к ней обращаются, Windows выделяет стеку больше памяти. Если вы попытаетесь использовать пространство сверх того, что было зарезервировано для стека, вы получите исключение переполнения стека. Страница MSDN для VirtualAlloc содержит более подробные сведения о резервировании и фиксации.

  2. x86 предъявляет строгие требования к стеку (например, должен расти вниз). Другие архитектуры более гибкие. Почти все операционные системы на базе x86 используют стек одинаково. Можно пойти с другой архитектурой стека. Вы не могли бы использовать какую-либо поддержку стека в x86, вам пришлось бы делать это самостоятельно, но вам пришлось бы конвертировать в традиционный стек при вызове в любой API ОС.

  3. Информация хранится в .exe. Вы можете настроить его с помощью флага линкера . Кроме того, API CreateThread позволяет изменять размер стека.

  4. Стеки потоков создаются во время создания потоков с использованием значений по умолчанию в .exe или из значений, указанных в вызове CreateThread.

Переполнение буфера в стеке происходит, когда вы переполняете буфер фиксированной длины, хранящийся в стеке, и перезаписываете другие управляющие данные в стеке, например адрес возврата.

Например, предположим, что вы вызываете функцию, и она имеет локальный буфер размером 16 байт. Стек вызовов может выглядеть следующим образом (другие детали опущены для ясности:

0x1000 - Return address
0x990 - Buffer

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

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