Сегментная защита памяти

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск

Сегментная защита памяти — один из вариантов реализации защиты памяти в процессорах архитектуры x86. Может применяться в защищенном режиме процессоров начиная с Intel 80286 и совместимых.

При использовании плоской модели памяти (англ. flat model, применяется многими современными ОС), каждый сегмент описывает весь объем виртуальной памяти. В такой модели защита памяти реализуется дополнительно страничной памятью.[1]

Определения[править | править код]

Существует четыре уровня привилегий (PL) сегментов (0-3). Привилегированность увеличивается с уменьшением номера (то есть нулевой — самый привилегированный). Уровни 0-2 соответствуют супервизору (Supervisor), 3 — пользователю (user). На нулевом уровне позволяется использование привилегированных инструкций.

Уровень привилегий сегмента (DPL англ. Descriptor Privilege Level) соответствует значению поля DPL в дескрипторе сегмента.

Текущий уровень привилегий (CPL англ. Current Privilege Level) соответствует уровню привилегий сегмента кода, селектор которого загружен в регистр CS (то есть уровню привилегий выполняющегося сегмента кода).

Запрашиваемый уровень привилегий (RPL англ. Requested Privilege Level) находится в двух младших битах селектора (задаётся программой).

Условия и принцип защиты[править | править код]

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

Условия защиты следующие:

  • Нельзя обращаться за пределы сегмента, обозначенные лимитом[2];
  • В сегментные регистры ES, CS, SS, DS, FS, GS нельзя загружать селекторы, указывающие на системные сегменты (в которых бит S=0);
  • В регистры ES, DS, FS, GS можно загружать селекторы таких сегментов, у которых DPL>=Max(CPL, RPL);
  • В регистры ES, DS, FS, GS можно загружать только селекторы сегментов данных или сегментов кода для выполнения/чтения;
  • В регистр CS нельзя загружать селектор сегмента данных;
  • В регистр CS можно загружать селектор сегмента кода только при CPL=DPL;
  • В регистр SS можно загружать только селектор сегмента данных для чтения/записи с DPL = CPL;
  • В сегмент кода нельзя писать;
  • В сегмент данных только для чтения нельзя писать;
  • В системные регистры LDTR, TR можно загружать только селекторы сегментов LDT и TSS соответственно;

Взаимодействие уровней привилегий[править | править код]

Взаимодействие четырёх уровней между собой осуществляется посредством шлюзов. Дескриптор шлюза так же, как и дескриптор сегмента имеет поле PL. Обращение к шлюзу подчинено вышеописанным правилам, но существуют дополнительные правила, накладывающие естественные ограничения на сегмент кода/TSS, указанный в шлюзе:

  • DPL вызываемого сегмента не должен быть численно больше CPL;
  • DPL шлюза не должен быть численно меньше DPL указанного в нём сегмента;
  • В шлюзе должен быть указан только сегмент кода или TSS (если это шлюз задачи);

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

Push Flags ; Только для шлюзов прерывания и ловушки
Push CS
Push IP
Jmp FAR Gate_Address

Переключение стека[править | править код]

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

Сегмент TSS содержит три дальних адреса стека — для уровней привилегий 0 — 2.

При переходе с больших на меньшие уровни через шлюзы происходит смена привилегий, и, соответственно, переключение стеков.[3]

Алгоритм переключения выглядит несколько сложнее:

  • Из соответствующих полей TSS загружаются новые значения SS, (E)SP;
  • В новый стек сохраняются предыдущие значения SS, (E)SP (до вызова);
  • Если шлюз является шлюзом вызова, то в новый стек копируются WC слов/двойных слов из стека вызывающей программы;
  • Если это шлюз прерывания или ловушки, то сохраняется значение (E)Flags;
  • Сохраняется текущее значение CS, (E)IP;
  • Из дескриптора шлюза в регистры CS:(E)IP загружается новый дальний адрес, указанный в соответствующих полях дескриптора;

При выполнении команды RetF/IRet:

  • Процессор извлекает из стека дальний адрес возврата (и регистр флагов);
  • Если у команды RetF есть параметр, то увеличивает значение (E)SP на его значение (удаляет из стека переданные процедуре параметры);
  • Анализирует RPL селектора CS[4]: если он численно больше CPL, то извлекает из стека дальний адрес прежней вершины стека и загружает его в SS:(E)SP. Таким образом происходит возврат к старому стеку;

Примечания[править | править код]

  1. The Unabridged Pentium 4 IA32 Processor Genealogy, Addison Wesley, 2004 ISBN 0-321-24656-X Chapter 13. The Flat Model "It should be noted that virtually all modern OSs utilize the Flat Model.", "There is no way to disable the IA32 processor's segmentation logic. However, if all segments are described (in the GDT) as read/writable, starting at location 00000000h and as 4GB in length, segmentation is effectively eliminated." "If segmentation is eliminated and Paging is used, the Paging Unit can provide complete protection, ... the following checks on each memory access attempt"
  2. Существует два вида сегментов: растущие вверх и растущие вниз. Допустимый диапазон сегментных адресов для растущих вверх сегментов: от нуля до лимита включительно. Для растущих вниз: от лимита (включительно) до $FFFFF при G=0 или до $FFFFFFFF при G=1.
  3. Всякий раз, когда происходит переход с большего на меньший уровень, стек меньшего уровня инициализируется заново.
  4. RPL, загруженный в CS всегда равен CPL

См. также[править | править код]

Ссылки[править | править код]