2016-08-28 11 views
5

Каждый поток имеет свой собственный стек для хранения локальных переменных. Но при вызове функции стеки также используются для store return addresses.Будет ли повышение стека вызовов увеличивать буферные переполнения?

В сборке x86 esp указывает на самый недавно выделенный конец стека. Сегодня большинство процессоров имеют стек, растущий отрицательно. Такое поведение позволяет выполнять произвольное выполнение кода, переполняя буфер и перезаписывая сохраненный адрес возврата. Если бы стек должен был расти положительно, такие атаки были бы невозможны.

Безопаснее ли, чтобы стек вызовов расти вверх? Почему Intel спроектировала 8086 с ростом стека вниз? Могли ли они изменить что-либо в каких-либо более поздних процессорах, чтобы современные x86 имели стеки, которые растут вверх?

+0

Возможно, ваш вопрос лучше подходит для [Security StackExchange.] (Http://security.stackexchange.com/) –

+2

@RyanB Я так не думаю. Вопрос в том, почему стек растет, несмотря на * проблему безопасности (та, которая на самом деле не существовала «назад», когда был разработан x86). –

+0

Учитывая, что 8086 был просто рожден как скромный микроконтроллер, я сомневаюсь, что кто-то рассмотрел последствия для безопасности растущего стека для защиты от ненадежных данных, поступающих от злоумышленников во всемирной сети. ;-) –

ответ

3

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

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

unsafe() { 
    char buf[128]; 
    gets(buf);  // stack grows upward: exploit happens when gets executes `ret` 
    // stack grows down: exploit happens when the `ret` at the end of *this* function executes. 
} 

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

Однако некоторые другие распространенные причины переполнения буфера могут быть легко встроены, например, strcat. Восходящие стеки помогут иногда.

Меры безопасности не должны быть надежными, чтобы быть полезными, поэтому это определенно поможет иногда. Вероятно, недостаточно для того, чтобы кто-то захотел изменить существующую архитектуру, такую ​​как x86, но интересная идея для новых архитектур. Тем не менее, Stack-grows-down является почти универсальным стандартом для процессоров. Использует ли что-нибудь растущий стек вызовов вверх? Насколько программное обеспечение действительно зависит от этого предположения? Надеюсь, не так много ...


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

Предсказуемые адреса кода/данных более важны, чем предсказуемые адреса стека, поэтому компьютер с большим количеством ОЗУ может уложить стек дальше от данных/кода, а при загрузке кода/данных с постоянным адресом. (Это очень волнительно. Я считаю, что мне повезло не писать реальные 16-битные программы, а только узнавать, но не использовать сегментацию. Возможно, кто-то, кто все еще помнит DOS, может пролить свет на то, почему он хорошо работает, чтобы стек с высоким адресом, вместо растущего вверх уровня в нижней части вашего сегмента и данных/кода вверху, например, с «крошечной» моделью кода, где все находится в одном сегменте).


Единственный реальный шанс изменить это поведение было с AMD64, который в первый раз x86 когда-либо действительно сломана обратной совместимости. Современные процессоры Intel по-прежнему поддерживают 8086 недокументированных кодов операций, таких как D6: SALC (Set AL from Carry Flag), что ограничивает пространство для кодирования для расширений ISA. (например.SSSE3 and SSE4 instructions would be 1 byte shorter, если Intel отказалась от поддержки недокументированных кодов операций.

Даже тогда это будет только для нового режима; Процессоры AMD64 по-прежнему должны поддерживать устаревший режим, а в 64-битном режиме они должны смешивать длинный режим с режимом сопоставления (обычно для запуска 32-разрядных процессов пользовательского пространства из 32-разрядных двоичных файлов).

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

Это позор, потому что есть много мелких вещей, которые они могли бы сделать, которые, вероятно, не требовали бы слишком большого количества дополнительных транзисторов в исполнительных устройствах. (например, в длинном режиме, замените setcc r/m8 на setcc r/m32).

+0

Если стек растет положительно с самого начала, было бы лучше? –

+0

Также ... Если вызов 'get' встраивается, проблем с безопасностью не будет. –

+0

@KelvinZhang: вы имеете в виду, если бы у 8086 были растущие стеки? Это не изменит ничего для первой части моего ответа, показывая, что типичный C все еще уязвим. Средняя часть моего ответа предлагает теорию о том, почему растущие стеки были распространены, особенно перед виртуальной памятью. –

 Смежные вопросы

  • Нет связанных вопросов^_^