Архитектура и дизайн
С точки зрения защиты архитектура x86 основана на иерархических кольцах, в соответствии с которыми все пространство выполнения, предоставляемое процессором, делится на четыре домены иерархической защиты, каждому из которых назначен свой уровень привилегий. Этот дизайн предполагает, что большая часть временного кода будет выполняться в домене с наименьшими привилегиями, а иногда будут запрашиваться службы из домена с более высокими привилегиями, и эти службы будут вытеснять менее привилегированные действия в стеке, а затем восстанавливать их таким образом, чтобы все приоритетное прерывание будет невидимым для менее привилегированного кода.
В структуре доменов иерархической защиты указано, что управление не может произвольно передаваться между разными доменами безопасности.
Шлюз - это особенность архитектуры x86 для передачи управления от менее привилегированных сегментов кода к более привилегированным, но не наоборот. Кроме того, точка в менее привилегированном сегменте, откуда будет передаваться управление, может быть произвольной, но точка в более привилегированном сегменте, куда будет передаваться управление, строго определена. Обратный переход управления к менее привилегированному сегменту разрешен только с помощью инструкции IRET
. В связи с этим руководство разработчика программного обеспечения Intel утверждает:
Модули кода в сегментах с более низкими привилегиями могут получить доступ только к модулям, работающим в сегментах с более высокими привилегиями, посредством строго контролируемого и защищенного интерфейса, называемого шлюзом. Попытки получить доступ к сегментам с более высокими привилегиями без прохождения через шлюз защиты и без достаточных прав доступа вызывают генерацию исключения общей защиты (#GP
).
Другими словами, шлюз - это точка входа в привилегированный домен с необходимыми правами доступа и целевым адресом. Таким образом, все шлюзы похожи и используются почти для одних и тех же целей, и все дескрипторы ворот содержат поле DPL, которое используется процессором для управления правами доступа. Но обратите внимание, что процессор проверяет DPL шлюза только в том случае, если источником вызова была программная инструкция CALL
, JMP
или INT
, и обходит эту проверку, когда источником вызова является аппаратное обеспечение.
Типы ворот
Несмотря на то, что все ворота похожи, у них есть некоторые отличия, потому что изначально инженеры Intel думали, что разные ворота будут использоваться для разных целей.
Ворота задач
Шлюз задач может храниться только в IDT и GDT и вызываться инструкцией INT
. Это особенный вид ворот, который существенно отличается от других.
Первоначально инженеры Intel думали, что революционизируют многозадачность, предоставив возможность переключения задач на базе ЦП. Они представили TSS (сегмент состояния задачи), который хранит состояние регистров задачи и может использоваться для аппаратного переключения задач. Есть два способа инициировать переключение аппаратных задач: с помощью самого TSS и с помощью Task Gate. Чтобы переключить аппаратную задачу, вы можете использовать инструкции CALL
или JMP
. Если я правильно понимаю, основная причина введения ворот задач заключалась в том, чтобы иметь возможность запускать аппаратные переключатели задач в ответ на поступление прерывания, потому что аппаратный переключатель задач не может быть запущен JMP
для селектора TSS.
На самом деле ни этим, ни аппаратным переключением контекста никто не пользуется. На практике эта функция не оптимальна с точки зрения производительности и неудобна в использовании. Например, учитывая, что TSS может храниться только в GDT, а длина GDT не может быть больше 8192, мы не можем иметь более 8k задач с аппаратной точки зрения.
Ворота-ловушки
Trap Gate может храниться только в IDT и вызываться инструкцией INT
. Его можно считать основным видом ворот. Он просто передает управление по конкретному адресу, указанному в дескрипторе шлюза-ловушки в более привилегированном сегменте, и ничего более. Затворы-ловушки активно используются для разных целей, в том числе:
- реализация системного вызова (например, Linux использует
INT 0x80
, а Windows использует INT 0x2E
для этих целей)
- реализация обработки исключений (у нас нет причин отключать прерывания в случае исключения).
- реализация обработки прерываний на машинах с APIC (мы можем лучше управлять стеком ядра).
Прерывание ворот
Шлюз прерывания может храниться только в IDT и вызываться инструкцией INT
. Он аналогичен шлюзу-ловушке, но, кроме того, вызов шлюза прерывания дополнительно запрещает прием прерывания в будущем путем автоматической очистки флага IF в регистре EFLAGS.
Шлюзы прерываний активно используются для реализации обработки прерываний, особенно на машинах на базе PIC. Причина - требование контролировать глубину стека. PIC не имеет функции приоритета источников прерывания. Из-за этого по умолчанию PIC отключает только те прерывания, которые уже обрабатываются процессором. Но другие прерывания все еще могут прибыть посередине и прервать обработку прерывания. Таким образом, в стеке ядра может быть одновременно 15 обработчиков прерываний. В результате разработчики ядра были вынуждены либо значительно увеличить размер стека ядра, что приводит к потере памяти, либо быть готовыми к случайному переполнению стека ядра. Interrupt Gate может гарантировать, что в стеке ядра одновременно может находиться только один обработчик.
Call Gate
Шлюз вызова может храниться в GDL и LDT и вызываться инструкциями CALL
и JMP
. Подобно шлюзу-ловушке, но дополнительно может передавать ряд параметров из стека задач пользовательского режима в стек задач режима ядра. Количество переданных параметров указывается в дескрипторе шлюза вызова.
Ворота вызова никогда не пользовались популярностью. На то есть несколько причин:
- Их можно заменить воротами-ловушками (бритва Оккама).
- Они не очень портативны. Другие процессоры не имеют таких функций, что означает, что поддержка шлюзов вызовов для системных вызовов является обузой при переносе операционной системы, поскольку эти вызовы должны быть перезаписаны.
- Они не слишком гибкие из-за того, что количество параметров, которые могут передаваться между стеками, ограничено.
- Они не оптимальны с точки зрения производительности.
В конце 1990-х годов Intel и AMD представили дополнительные инструкции для системных вызовов: _16 _ / _ 17_ (Intel) и _18 _ / _ 19_ (AMD). В отличие от шлюзов вызова, новые инструкции обеспечивают повышение производительности и нашли применение.
Резюме
Я не согласен с Майклом Фукаракисом. Извините, но нет никаких различий между прерываниями и ловушками, кроме влияния на флаг IF
.
Теоретически каждый тип шлюза может служить интерфейсом, указывающим на сегмент с любым уровнем привилегий. На практике в современной операционной системе используются только шлюзы прерываний и ловушек, которые используются в IDT для системных вызовов, прерываний и обработки исключений, и благодаря этому все они служат точкой входа в ядро.
Любой тип шлюза (включая прерывание, ловушку и задачу) может быть вызван программно с помощью инструкции INT
. Единственная функция, которая может запретить доступ кода пользовательского режима к определенным воротам, - это DPL. Например, когда операционная система создает IDT, независимо от типов конкретных шлюзов, ядро устанавливает DPL шлюзов, которые будут использоваться для обработки аппаратных событий, на 0, и в соответствии с этим доступ к этим воротам будет разрешен только из пространства ядра. (который работает в наиболее привилегированном домене), но когда он настраивает шлюз для системного вызова, он устанавливает DPL на 3, чтобы разрешить доступ к этому шлюзу из любого кода. В результате задача пользовательского режима может выполнять системный вызов с использованием шлюза с DPL = 3, но, например, обнаружит общую ошибку защиты при попытке вызвать обработчик прерывания клавиатуры.
Любой тип шлюза в IDT может быть вызван аппаратно. Люди используют шлюзы прерывания для обработки этих аппаратных событий только в тех случаях, когда они хотят добиться некоторой синхронизации. Например, чтобы убедиться, что переполнение стека ядра невозможно. Например, у меня есть успешный опыт использования ловушек для обработки аппаратных прерываний в системе на основе APIC.
Аналогичным образом любой тип шлюза в IDT можно вызвать программно. Причина использования ловушек для системных вызовов и исключений проста. Никаких причин отключать прерывания. Отключение прерывания - это плохо, потому что оно увеличивает задержки обработки прерывания и увеличивает вероятность потери прерывания. Из-за этого никто их не выведет из строя без серьезного повода.
Обработчик прерывания обычно пишется в строгом стиле реентерабельности. Таким образом, обработчики прерываний обычно не обмениваются данными и могут прозрачно вытеснять друг друга. Даже когда нам нужно взаимно исключить одновременный доступ к данным в обработчике прерывания, мы можем защитить только доступ к совместно используемым данным с помощью инструкций cli и sti. Нет никаких причин рассматривать весь обработчик прерывания как критическую секцию. Нет никаких причин для использования шлюзов прерывания, кроме желания предотвратить возможное переполнение стека ядра в системах на основе PIC.
Ловушки - это решение по умолчанию для взаимодействия с ядром. Шлюз прерывания может использоваться вместо шлюза-ловушки, если для этого есть серьезная причина.
person
ZarathustrA
schedule
23.12.2012