OSDev Wiki
Advertisement

Глава 4

Защита.[]

В защищенном режиме архитектур Intel 64 и IA-32 существует механизм защиты, который работает и на уровне сегментов и на уровне страниц. Этот механизм защиты позволяет ограничить доступ к определенным сегментам или страницам. Этот механизм основан на уровнях привилегий (четыре уровня привилегий для сегментов и два уровня привилегий для страниц). Например, важный код и данные операционной системы могут быть защищены, путем их размещения в более привилегированных сегментах чем те, которые содержат код приложений. В этом случае механизм защиты процессора будет препятствовать тому, чтобы код прикладных программ смог бы получить доступ к коду операционной системы и любым данным, но будет существовать контролируемый доступ, реализуемый некоторым образом. Сегментный и страничный механизмы защиты также могут использоваться на всех стадиях разработки программного обеспечения, чтобы помочь в локализации и обнаружении проблем проектирования и ошибок. Они также могут быть включены в конечные продукты, чтобы снабдить надежностью: операционные системы, программы утилиты, и прикладное программное обеспечение. Когда используется механизм защиты, каждое обращение к памяти проверяется, чтобы убедиться в том что оно удовлетворяет различным проверкам защиты. Все проверки осуществляются до того, как цикл обращения к памяти начнет выполняться; любое нарушение приводит к исключению. Поскольку проверки выполняются параллельно с передачей адреса, это не вызывает никакого штрафа в работе. Все категории проверок, которые осуществляются перечислены в списке:

  • Проверки предела.
  • Проверки типа.
  • Проверки уровня привилегий.
  • Ограничения адресуемой области.
  • Ограничения точек входа процедуры.
  • Ограничения набора команд.

Любые нарушение защиты приводит к генерации исключения. Для объяснения работы механизма исключения см. Главу 5, “Обработка прерываний и исключений”. А текущая глава описывает механизмы защиты, нарушения которых приводит к исключениям. Ниже следующие разделы описывают механизм защиты, доступный в защищенном режиме. Для информации относительно защиты в режимах реальных адресов и виртуального-8086 См. Главу 15, “Эмуляция 8086”,.


Включение и отключение сегментного и страничного механизмов защиты.[]

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

Часть механизма сегментной защиты, которые базируются на уровнях привилегий, могут по существу быть заблокированы, путем назначения уровня привилегий 0 (самый привилегированный) для всех селекторов сегментов и дескрипторов сегментов. Это действие отключает барьеры защиты на уровне привилегий между сегментами, но другие проверки защиты, такие как проверка предела и контроля соответствия типов все еще действуют. Защита на уровне страниц автоматически включается, как только страничный механизм будет включен (путем установки флага PG в регистре CR0). Здесь снова нет никакого бита режима для того, чтобы выключить защиту на уровне страниц, как только страничный механизм был активирован. Однако, защита уровня страницы может быть заблокирована, если выполнить следующие операции:

  • Очистку флага WP в регистре контроля CR0.
  • Установку флагов чтение/запись (R/W) и пользователя/супервизор (U/S) для всех каталогов страниц и для всех полей таблицы страниц.

Это действие делают каждую страницу перезаписываемой, пользовательской страницей, что приводит к эффекту отключает защиты на уровне страниц.

Поля и флаги, используемые для защиты на уровне сегментов и уровне страниц.[]

Механизмы защиты процессора, используют следующие поля и флаги в структурах данных системы, чтобы управлять доступом к сегментам и страницам:

  • Флаг дескрипторный тип — (Бит 12 во втором двойном слове дескриптора сегмента). Определяет, является ли дескриптор, дескриптором сегмента системы или сегментом данных или сегментом код.
  • Поле типа — (Биты 8 - 11 во втором двойном слове дескриптора сегмента). Определяет тип сегмента: код, данные, или сегмент системы.
  • Поле предела — (Биты с 0 по 15 из первого двойного слова и биты с 16 по 19 из второго двойного слова дескриптора сегмента). Определяет размер сегмента, вместе с флагом G и флагом E (для сегментов данных).
  • Флаг G — (Бит 23 во втором двойном слове дескриптора сегмента.) Определяет размер сегмента, вместе с полем предела и флагом E (для сегментов данных).
  • Флаг E — (Бит 10 во втором двойном слове дескриптора сегмента данных). Определяет размер сегмента, вместе с полем предела и флагом G.
  • Поле, уровень привилегии дескриптора (Descriptor privilege level - DPL) — (Биты 13 и 14 во втором двойном слове дескриптора сегмента). Определяет уровень привилегии сегмента.
  • Поле, требуемый уровень привилегии (Requested privilege level - RPL) — (Биты 0 и 1 из любого селектора сегмента). Определяет требуемый уровень привилегии сегментным селектором.
  • Поле, текущий уровень привилегии (Current privilege level - CPL) — (Биты 0 и 1 из сегментного регистра CS). Указывает уровень привилегии в настоящее время выполняющейся программы или процедуры. Термин текущий уровень привилегии (CPL) указывает на состояние этой области.
  • Флаг пользователь/супервизор (User/supervisor - U/S) — (Бит 2 из поля таблицы страниц или директории страниц). Определяет тип страницы: пользователь или супервизор.
  • Флаг чтение/запись (Read/write - R/W) — (Бит 1 из поля таблицы страниц или директории страниц). Определяет тип доступа, разрешенного этой странице: только чтение или чтение - запись.

Рисунок 4-1 показывает местоположение различных полей и флагов в дескрипторах сегментов: данных, кода, и системы; Рисунок 3-6 показывает местоположение поля RPL (или CPL) в селекторе сегмента (или регистре CS); и рисунок 3-14 показывает местоположение U/S и флагов R/W в полях таблицы страниц и каталоге страниц.


Том 3 рис 4.1

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

Дескриптор сегмента кода в 64-битном режиме.[]

В 64-битном режиме сегменты кода продолжают существовать, хотя при вычислении адреса, база сегмента трактуется как ноль. Некоторое содержимое дескриптора сегмента кода (CS) (базовый адрес и поле предела) игнорируются; остальные поля обычно функционируют (за исключением бита позволяющего читать(readable) из поле типа). Дескрипторы сегмента кода и селекторы необходимы в режиме IA-32e, чтобы процессор мог находиться в рабочем режиме и иметь возможность выполнять код на привилегированном уровне. Используются как:

  • В режим IA-32e используется ранее неиспользованный бит в дескрипторе CS. Бит 53 определенный как 64-битовый (L) флаг и используется, чтобы выбрать между 64-битовым способом и способом совместимости, когда способ IA-32e является активным (IA32_EFER.LMA = 1). См. Рисунок 4-2.
    • Если CS.L = 0 и режим IA-32e является активным, процессор находится в режиме совместимости. В этом случае, CS.D выбирает размер по умолчанию для данных и адресов. Если CS.D = 0, данные по умолчанию и размер адреса составляет 16 битов. Если CS.D = 1, по умолчанию размер данные и размер адреса составляет 32 бита.
    • Если CS.L = 1 и режим IA-32e является активным CS.D = 0, единственное корректное значение. Это значение указывает размер операнда по умолчанию 32 битов и размер адреса по умолчанию 64 бита. CS.L = 1 и CS.D = 1 этот набор двоичных знаков зарезервирован для будущего использования, поэтому будет генерироваться ошибка #GP при попытке использовать сегмент кода с таким набором битов в режиме IA-32e.
  • В режиме IA-32e DPL из дескриптора CS используется для проверки привилегии во время работы (как в устаревшем 32-битном режиме).

Том 3 рис 4.2

Проверка предела.[]

Поле предел в дескрипторе сегмента, препятствует тому, чтобы программы или процедуры смогли бы обратиться к адресам памяти вне сегмента. Действующее значение предела зависит от состояния флага G (granularity - гранулярность) (см. рисунок 4-1). Для сегментов данных предел также зависит от флага E (expansion direction - направление расширения) флаг и B (размер указателя стека по умолчанию и/или верхняя граница). Флаг E - один из битов в поле типа, когда дескриптор сегмента принадлежит классу сегмента данных. Когда флаг G очищен (выбрана байтовая гранулярность), действующий предел это значение в 20-битовом поле предела в дескрипторе сегмента. Здесь, предел находиться в диапазоне от 0 до FFFFFH (1 МБайт). Когда флаг G установлен (выбрана 4-КБитная страничная гранулярность), процессор масштабирует значение в поле предела на коэффициент 2^12 (4 КБайта). В этом случае, действующий предел колеблется от FFFH (4 КБайта) к FFFFFFFFH (4 ГБайтам). Отметьте, что, когда используется масштабирование (флаг G установлен), младшие 12 битов смещения(адрес) сегмента не проверяются пределом; например, отметьте, что, если предел сегмента 0, смещение от 0 до FFFH целиком действителен. Для всех типов сегментов кроме расширяющихся вниз сегментов данных действующий предел это последний адрес сегмента, к которому разрешен доступ. Который меньше на единицу чем размер, в байтах, сегмента. Процессор вызывает исключение общей защиты любое время, при попытка получить доступ к следующим адресам в сегменте:

• Смещение байта(byte), больше чем действующий предел

• Смещение слова(word), больше чем (действующий предел – 1)

• Смещение двойного слова(DWord), больше чем (действующий предел – 3)

• Смещение четверичного слова(QWord), больше чем (действующий предел – 7)


Для вниз растущих сегментов данных, предел сегмента имеет ту же самую функцию, но интерпретируется по-другому. Здесь, действующий предел определяет последний адрес, к которому не разрешено получит доступ в пределах сегмента; Если флаг B установлен диапазон корректных смещений от (действующий предел + 1) к FFFFFFFFH, и если флаг B очищен от (действующей предел + 1) к FFFFH. У вниз расширенного сегмента будет максимальный размер, когда предел сегмента 0.

Предел проверяет и ловит такие программные ошибки как: выход из-под контроля кода, выход из-под контроля индекса массива, и некорректные вычисления указателя. Эти ошибки будут обнаружены, когда они произойдут, таким образом идентификация причины становиться легче. Без проверки предела эти ошибки могли переписать код или данные в другом сегменте. В дополнение к проверке пределов сегмента процессор также проверяет пределы таблицы дескрипторов. GDTR и регистры IDTR содержат 16-битные значения предела которые используются процессором, чтобы препятствовать тому, чтобы программы смогли бы выбрать дескрипторы сегмента вне соответствующих таблиц дескрипторов. LDTR и регистры задачи содержат 32-битные значения предела сегмента (прочитанный от дескрипторов сегмента для текущего LDT и TSS, соответственно). Процессор использует эти пределы сегмента, чтобы предотвратить доступы вне границ текущего LDT и TSS. См. Главу 3.5.1, “Таблицы дескрипторов Сегмента,” за дополнительной информацией по полю придела для GDT и LDT; см. Главу 5.10, “Таблица дескрипторов прерываний (IDT),” для получения дополнительной информации о поле предела IDT; и см. Главу 6.2.4, “Регистр Задачи,” для получения дополнительной информации о поле предела для TSS сегмента.


Проверка предела в 64-битовом режиме.[]

В 64-битовом режиме, во время роботы процессор не выполняет проверку пределов для сегментов кода и данных. Однако, процессор выполняет проверки пределов таблицы дескрипторов.

Проверка типа дескриптора.[]

Дескрипторы сегментов содержа информацию о типе в двух местах:

  • Флаг S (дескрипторный тип).
  • Поле типа.

Процессор использует эту информацию, чтобы обнаружить программные ошибки, которые приводят к попытке использовать сегмент или точек входа в неправильной или непреднамеренной манере. Флаг S указывает, имеет ли дескриптор тип: системы или кода или имеет тип данных. Поле типа обеспечивает еще 4 бита для того чтобы различить типы дескрипторов, такие как: дескриптор кода, данных, и системы. Таблица 3-1 показывает описание поля типа для дескрипторов данных и кода; Таблица 3-2 показывает описание поля типа для дескрипторов системы.

Процессор исследует информацию о типе многократно. Когда работает с сегментными селекторами и дескрипторами сегментов. Следующий список дает примеры типичных операций, где выполняется контроль над соответствием типов(этот список не является исчерпывающим):

  • Когда сегментный селектор загружается в сегментный регистр — Определенные сегментные регистры могут содержать только определенные дескрипторные типы, например:
    • В CS регистр может быть загружен только селектор для сегмента кода.
    • Селекторы сегмента кода, которые не предназначены для чтения или которые предназначены для системных сегментов, не могут быть загружены в регистры сегмента данных (DS, ES, FS, и GS).
    • Только селекторы сегмента, в которых разрешена запись данных могут быть загружены в регистр SS.
  • Когда селектор сегмента загружается в регистр LDTR или регистр задачи — Например:
    • В регистр LDTR может быть загружен только селектор для LDT.
    • Регистр задачи может быть загружен только сегментный селектор для TSS.
  • Когда команды обращаются к сегментам, дескрипторы которых уже загружены в сегментные регистры — Определенные сегменты могут использоваться командами только определенными способами, например:
    • Ни одна машинная команда не может писать в выполнимый сегмент.
    • Ни одна машинная команда не может писать в сегмент данных, если он не является перезаписываемым.
    • Ни одна машинная команда не может читать выполнимый сегмент, если флаг разрешения чтения(readable) не установлен.
  • Когда операнд машинной команды содержит селектор сегмента — определенные команды могут обратиться к сегментам или точкам входа только определенного типа, например:
    • Дальний вызов(far CALL) или далекая прыжок(far JMP) могут обратиться только к дескрипторам сегмента для приспособленца кодового сегмента, не приспособленца кодового сегмента, точка входа по вызову, точка входу в задачу, или TSS.
    • Машинная команда LLDT должна ссылаться на дескриптор сегмента такой как LDT.
    • Машинная команда LTR должна ссылаться на дескриптор сегмент такой как TSS.
    • Машинная команда LAR должна ссылаться на один из сегментов или точку входа, такие как: LDT, TSS, точка входа по вызова, точка входу в задачу, сегмент кода или сегмент данных.
    • Машинная команда LSL должна ссылаться на дескриптор сегмента такой как: LDT, TSS, сегмент кода или сегмент данных.
    • Поля таблицы IDT должны содержать одно из: точку входа в прерывание, точку входа в ловушку, или точку входа в задачу.
  • Во время выполнения определенных внутренних операций — Например:
    • На таких как дальний вызов или дальний прыжок (выполняемые при помощи машинных инструкций far CALL или far JMP). Процессор, определяет какой тип команды передачи управления, будет выполнятся (вызов или скачок к другому сегменту кода, вызов или скачок через точку входа, или переключение задачи). И проверяет поле типа в сегменте (или точке входа) дескриптора селектор которого получен как операнд в инструкциях CALL или JMP, и который указывает на сегмент (или точку входа). Если дескрипторный тип один из: сегментом кода, точкой входа по вызову - то вызов или скачок к другому сегменту будет соответствующем; если дескрипторный тип будет типом TSS или точкой входа в задачу, то будет выполнено переключение на соответствующею задачу.
    • На таких как вызов или скачок через точку входа по вызову в процедуру (или на обработчик прерывания или обработчик исключения вызванные через точку входа в ловушку или точку входа в прерывание), процессор автоматически проверяет, что дескриптор сегмента, указывает на точку входа имеет тип сегмент кода.
    • На таких как вызов или скачок к новой задаче через точку входа в задачу (или на обработчик прерывания или обработчик исключения вызывающей новую задачу через точку входа в задачу), процессор автоматически проверяет, что дескриптор сегмента, указанный точкой входа в задачи, имеет тип TSS.
    • По возвращению от вложенной задачи (инициированной инструкцией IRET), процессор проверяет, что предыдущая поле связи(link) задачи в текущем TSS указывает на дескриптор типа TSS.

Проверка нулевого селектора сегмента.[]

Попытка загрузить нулевого селектора сегмента (см. Секцию 3.4.2, “Селекторы Сегмента”) в CS или регистр сегмента SS производит исключение общей защиты (#GP). Нулевой селектор сегмента может быть загружен в DS, ES, FS, или регистр GS, но при любой попытке получить доступ к сегменту через один из этих регистров, когда в него загружен нулевой селектор сегмента приведет в результате к таму что будет сгенерированно исключение #GP. Загрузка в неиспользуемые сегментные регистры данных нулевого селектора сегмента является полезным методом обнаружения доступов к неиспользуемым регистрам сегментов и/или предотвращению нежелательных доступов к сегментам данных.


Проверка нулевого сегмента в 64-битном режиме.[]

В 64-битном режиме, процессор не выполняет проверку нулевого селектора сегмента во время работы. Когда происходит попытка получить доступ к памяти, через сегментный регистр,в который загружен нулевой селектор сегмента, процессор не вызывает ошибку #GP,

Уровни привилегий.[]

Механизм защиты сегментов процессора распознает 4 уровня привилегии, пронумерованные от 0 до 3. Чем число больше тем меньше привилегии. Рисунок 4-3 показывает, как эти уровни привилегии могут интерпретироваться как кольца защиты. Центр (предназначен для самого привилегированного кода, данных, и стеков) используется для сегментов, содержащих критическое программное обеспечение, обычно это ядро операционной системы. Внешние кольца используются для менее критического программного обеспечения. (Системы, которые используют только 2 из 4 возможных уровней привилегии, должны использовать уровни 0 и 3.)

Том 3 рис 4.3

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

  • Текущий уровень привилегии (Current privilege level - CPL) — CPL это уровень привилегии который выполняется в настоящее время в программе или задаче. Он находится в битах 0 и 1 сегментных регистров CS и SS. Обычно, CPL равен уровню привилегии сегмента кода, от которого начинают приходить инструкции. Процессор изменяет CPL, когда контроль за программой передан сегменту кода с другим уровнем привилегии. Текущий уровень привилегии трактуется немного по-другому, нежели чем просто ограничения доступа к сегментам кода приспособленцам. Кодовый сегмент приспособленец может получить доступ от любого уровня привилегии, который равен или численно больше (менее привилегированный) чем DPL приспособленца кодового сегмента. Кроме того, CPL не изменяется, когда процессор получает доступ к нужному сегменту кода, у которого другой уровень привилегии чем текущий уровень привилегии.
  • Дескрипторный уровень привилегии (Descriptor privilege level - DPL) — DPL является уровнем привилегии сегмента или точки входа. Он находится в поле DPL сегмента или дескриптора точки входа для сегмента или точки входа соответственно. Когда в настоящее время выполняющийся сегмент кода пытается получить доступ к сегменту или точки входа, DPL сегмента или токи входа сравнивается с CPL и RPL сегмента или селектора точки входа (как описано позже в этой секции). DPL интерпретируется по-разному, в зависимости от типа дескриптора сегмента или дескриптора точки входа, к которому начинается обращение:
    • Сегмент данных — DPL указывает в цифровой форме самый высокий уровень привилегии, который программа или задача, может иметь для того чтобы доступ к сегменту был бы разрешен. Для примера, если DPL сегмента данных 1, только программы, работающие с CPL 0 или 1, могут получить доступ к сегменту.
    • Сегмент кода не приспособленец (не использующей точки входа по вызову) — DPL указывает уровень привилегии, который должны иметь программа или задача что бы получить доступ к сегменту. Например, если DPL сегмента кода не приспособленца равен 0, только программы, запущенные с CPL 0 , могут получить доступ к этому сегменту.
    • Точка входа по вызову — DPL указывает в цифровой форме самый высокий уровень привилегии, с которым в настоящее время может работать программа или задача и при этом все еще будут в состоянии получить доступ к точки входа по вызову. (Это - то же самое правило доступа что и у сегмента данных).
    • Сегмент кода приспособленец и неприспособленец доступ к которым осуществляется через точку входа по вызову — DPL указывает в цифровой форме самый низкий уровень привилегии, который возможно будет имеет программа или задача, и при этом ей будет позволено получить доступ к сегменту. Например, если DPL сегмента кода приспособленца 2, а программы работает с CPL 0 или 1, не с могут получить доступ к сегменту.
    • TSS — DPL указывает в цифровой форме самый высокий уровень привилегии, с которым может в настоящее время выполняться программа или задача и при этом все еще будет в состоянии получить доступ к TSS. (Это - то же самое правило доступа что касается сегмента данных).
  • Запрашиваемый уровень привилегии (Requested privilege level - RPL) — RPL также переопределяет уровень привилегии, который назначается селектору сегмента. Он находиться в битах 0 и 1 в селекторе сегмента. Процессор проверяет RPL наряду с CPL, чтобы определить, позволен ли доступ к сегменту. Если программа или задача запрашивает доступ к сегменту, и при этом имеет достаточно привилегий для доступа к сегменту, то доступ получит. Если RPL не имеет достаточного уровня привилегии, то доступ будет запрещен. Таким образом, если RPL селектора сегмента в цифровой форме больше чем CPL, RPL переопределяется через CPL, и наоборот. RPL может использоваться, чтобы гарантировать, что код, которому дают привилегию, не получает доступ к сегменту от имени прикладной программы, если у самой программы нет привилегий доступа для такого сегмента. См. Главу 4.10.4, “Проверка Привилегий Доступа вызывающий программы (Инструкция ARPL)”, для детального описания сути и типичного использования RPL.

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


Проверки уровня привилегий при доступе к сегментам данных.[]

Чтобы получить доступ к данным в сегменте данных, необходимо загрузить сегментный селектор для сегмента данных в сегментные регистры данных (DS, ES, FS, или GS) или в регистр управляющим стеком (SS). (Сегментные регистры могут быть загружены при помощи инструкций: MOV, POP, LDS, LES, LFS, LGS, и LSS.) Прежде, чем процессор успеет загрузить сегментный селектор в сегментный регистр, он выполнит проверку привилегии (см. рисунок 4-4), для этого он сравнит текущей уровни привилегии у действующей программы или задачи (CPL), RPL селекторного сегмента, и DPL дескриптора сегмента. Процессор загружает сегментный селектор в сегментный регистр , если DPL в цифровой форме больше чем или равен и CPL и RPL. Иначе будет сгенерирована ошибка общей защиты, и сегментный регистр будет не заполнен.

Том 3 рис 4.4

Рисунок 4-5 показывает четыре процедуры (расположенный в сегментах кодах A, B, C, и D), каждая, работает на различных уровнях привилегии и каждая попытке получить доступ к некоторому сегменту данных.

  1. Процедура в сегменте кода A в состоянии получить доступ к сегменту E данных используя селектор сегмента E1, потому что CPL сегмента кода A и RPL селектора сегмента E1 равен DPL сегмента данных E.
  2. Процедура в сегменте кода B в состоянии получить доступ к E сегмента данных используя селектор сегмента E2, потому что оба CPL сегмента кода B и RPL селектора сегмента E2 в цифровой форме ниже (более привилегированные), чем DPL данных сегмента E. Сегмент кода B процедуры может также получить доступ к сегменту данных E использующий селектор сегмента E1.
  3. Процедура в сегменте кода C не в состоянии получить доступ к сегменту данных E, используя селектор сегмента E3 (пунктирная линия), потому что оба CPL сегмента кода C и RPL селектор сегмента E3 в цифровой форме больше (менее привилегированны) чем DPL данных сегмента E. Даже если бы сегмент кода C процедуры использовал бы селектор сегмента E1 или E2, такой, что RPL был бы приемлемым, она все еще не смогла бы получить доступ к сегменту данных E, потому что его CPL не дает достаточно привилегий.
  4. Процедура в сегменте кода D казалось бы в состоянии получить доступ к E сегмента данных, потому что сегмента кода D имеет CPL в цифровой форме меньше чем DPL данных, сегмента E. Однако, RPL селектора сегмента E3 (который использует процедура сегмента кода D , чтобы получить доступ к данным, сегмента E) в цифровой форме больше, чем DPL данных сегмента E, таким образом доступ не позволен. Если бы сегмент кода D процедура использовал бы селектор сегмента E1 или E2, чтобы получить доступ к сегменту данных, доступ был бы разрешен.

Том 3 рис 4.5

Как продемонстрировано в предыдущих примерах, адресуемая область программы или задачи изменяется как изменяется ее CPL. Когда CPL будет 0, все сегменты данных на всех уровнях привилегии будут доступны; когда CPL будет 1, только сегменты данных на уровнях привилегии 1 — 3 будут доступны; когда CPL будет 3, только сегменты данных на 3 уровне привилегий будут доступны.


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


Важно отметить, что RPL селектора сегмента для сегмента данных находится под контролем программного обеспечения. Например, прикладная программа, работающая на текущем 3 уровне привилегий , может установить RPL для сегмента данных селектора в 0. С установленным RPL в 0, будут только проверки CPL,и не будет проверок RPL, которые обеспечат защиту против преднамеренных, прямых попыток нарушить безопасность на уровне привилегии для сегмента данных. Чтобы предотвратить такие типы нарушения проверок привилегии уровня, программа или процедура могут проверить привилегии доступа всякий раз, когда они получат селектор сегмента данных от другой процедуры (См. Главу 4.10.4, “Проверка Привилегий Доступа вызывающий программы (Инструкция ARPL)”).


Доступ к данным в сегментах кода.[]

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

  • Загрузите в регистр сегмента данных селектор сегмента не приспособленца, с разрешенным чтением, сегмент кода.
  • Загрузите в регистр сегмента данных селектор сегмента приспособленца, с разрешенным чтением, сегмент кода.
  • Использовать сегмент кода вместе префикс переопределения сегмента (CS), чтобы прочитать с разрешенным чтением, сегмент кода, селектор которого уже загружен в регистре CS.

Те же самые правила что и правила доступа к сегменту данных применяются и к первому способу. Второй способ всегда действителен, потому что уровень привилегии сегмента кода приспособленца, имеет практически такой же CPL, и не зависит от его DPL. Третий способ всегда действителен, потому что DPL сегмента кода, выбранного регистром CS, является таким же как CPL.

Проверка уровня привилегий при загрузки регистра SS.[]

Уровень привилегии также проверяется после того как в регистр SS был загружен сегментный селектор для сегмента стека. Здесь все уровни привилегии, относящиеся к сегментом стека, должны соответствовать CPL; то есть, CPL, RPL селектора сегмента стека, и DPL дескриптора сегмента стека должны быть одинаковыми. Если RPL и DPL не равны CPL, тогда будет сгенерированно исключение общей защиты (#GP).


Проверка уровня привилегий над кодом, когда происходит передача контроля над программой между сегментами кода.[]

Чтобы передать контроль над программой от одного сегмента кода к другому, нужно загрузить сегментный селектор ссылающийся на сегмент кода в сегментный регистр кода (CS). Одна из частей процесса загрузки, это когда процессор исследует дескриптор сегмента, который предназначен для будущего исполнения кода и выполняет различные проверки: предела, типа и проверки привилегии. Если эти проверки успешно пройдены, то произойдет загрузка регистр CS, контроль за программой будет передан новому сегменту кода, и выполнение программы начинается с инструкции, на которую указывает регистр EIP.


Передачи контроля над программой выполнены с помощью инструкций: JMP, CALL, RET, SYSENTER, SYSEXIT, INT n, и IRET, f так же при помощи механизмов исключений и прерываний. Исключения, перерывания, и инструкция IRET - особые случаи, обсуждаемые в Главе 5, “Обработка Исключений и Перерываний”. Текущая глава обсуждает только инструкции: JMP, CALL, RET, SYSENTER, и SYSEXIT. Инструкции JMP или CALL могут сослаться на другой сегмент кода любым из четырех способов:

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

Ниже следующие разделы описывают первые два типа ссылок. См. Секцию 6.3, “Переключение Задачи,” для информации относительно передачи управляя программе через точку входа в задачу и/или TSS.


Специальные инструкции SYSENTER и SYSEXIT предназначенные для того, чтобы сделать быстрые вызовы и возвращения к/от операционной системы или процедурам управления. Эти инструкции кратко обсуждаются в Главе 4.8.7, “Выполняем Быстрый Вызов Процедур Системы с Помощью Инструкциями SYSENTER и SYSEXIT”.


Прямые вызовы или прыжки к сегментам кода.[]

Инструкции в ближней (near) форме JMP, CALL, и RET передают контроль за программой в пределах текущего сегмента кода, таким образом проверки уровня привилегии не выполняются. Дальние (far) формы инструкций JMP, CALL, и RET передают контроль к другим сегментам кода, таким образом процессор должен выполнять проверки уровня привилегии. Когда передача контроль над программой передается другому сегменту, если при этом не используется точка входа по вызова в процедуру, то процессор исследует четыре вида уровня привилегии и информацию о типе (см. рисунок 4-6):

  • CPL (Здесь, CPL - уровень привилегии сегмента кода запроса; это, сегмент кода, который содержит процедуру, которая делает запрос или прыжок).
  • DPL дескриптора сегмента для сегмента кода к которому происходит обращение, который содержит вызываемую процедуру.
  • RPL селектора сегмента сегмента кода к которому происходит обращение.
  • Флаг приспособленец (conforming -C) в дескрипторе сегмента для сегмента кода к которому идет обращение. Этот флаг определяет, является ли сегмент приспособленцем (C, флаг установлен), или не приспособленцем (C флаг очищен), сегмент кода. См. Главу 3.4.5.1, “Дескрипторные Типы Сегмента данных и кода,” для дополнительной информацией об этом флаге.

Эти правила процессор использует, чтобы проверить зависимость CPL, RPL, и DPL от настройки флага C, как описано в следующих разделах.


Том 3 рис 4.6

Доступ к сегменту кода не приспособленца.[]

При доступе сегментам кода не приспособленца, CPL процедуры запроса должен быть равным DPL сегмента кода приспособленца; иначе, процессор производит исключение общей защиты (#GP). Например на рисунке 4-7:

  • Сегмент кода C является сегментам кода не приспособленцем. Процедура в сегменте кода A может вызывать процедуру в сегменте кода C (используя селектор сегмента C1), потому что они на том же самом уровне привилегии (CPL сегмента кода A равен DPL сегмента кода C).
  • Процедура в сегменте кода B не может вызывать процедуру в сегменте кода C (используя селектор сегмента C2 или C1), потому что эти два сегмента кода на различных уровнях привилегии.

Том 3 рис 4.7

RPL селектора сегмента, который указывает на сегмент кода не приспособленца, вносит ограничения в проверки привилегии. RPL должен быть в цифровой форме меньше чем или равным CPL процедуры запроса для успешной передачи контроля, что бы факт передачи произошел. Так, в примере на рисунке 4-7 RPL'ы селекторов сегмента C1 и C2 мог по праву быть установлен в 0, 1, или 2, но не в 3. Когда селектор сегмента сегмента кода не приспособленца загружен в регистр CS, область уровня привилегии не изменяется; то есть, он остается равным CPL (который относиться к уровню привилегии процедуры запроса). Это верно, даже если RPL селектора сегмента отличается от CPL.


Доступ к сегменту кода приспособленца.[]

Когда происходит доступ к сегменту кода приспособленца, CPL процедуры запроса должна быть в цифровой форме равным или больше чем (менее привилегированный) DPL сегмента кода приспособленца; процессор производит исключение общей защиты (#GP), только если CPL - меньше чем DPL. (Селектор сегмента, RPL для сегмента кода приспособленца не проверяется, Если сегмент кода является сегментом кода приспособленцем, то RPL селектора сегмента кода, который выбран в качестве места назначения).


В примере см рисунок 4-7 сегмент кода D является сегментом кода приспособленцем. Поэтому, процедуры запроса и в сегментах кода A и в B могут получить доступ к сегменту кода D (используя оба селектора сегмента D1 или D2, соответственно), потому что у них у обоих CPL'ы, которые больше чем или равны DPL соответствовавшего сегмента кода. Для сегментам кода приспособленца, DPL представляет из себя в цифровой форме самый низкий уровень привилегии с которым может быть процедура запроса, что бы успешно сделать вызов в сегменте кода.


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


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


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


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


Дескрипторы точек входа.[]

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

• Точки входа по вызову в процедуру - Call gates

• Точки входа в ловушки - Trap gates

• Точки входа в прерывания - Interrupt gates

• Точки входа в задачи - Task gates


В русской литературе встречаются переводы gate как шлюз.

Иногда я буду опускать и писать вместо "Точки входа по вызову в процедуру" "Точки входа по вызову"


Точки входа в задачи используются для переключения задач и обсуждены в Главе 6, “Управление Задачами”. Точки входа в ловушка и прерывания - специальные виды точек входа, используемых для того, чтобы вызвать обработчики исключений и прерываний. Это описано в Главе 5, “Обработчики прерываний и исключений.” Текущая глава сосредоточена только на точках входа по вызову в процедуру.


Точки входа по вызову в процедуру.[]

Точки входа по вызову облегчает управление передачей контроля за программой между различными уровнями привилегии. Они типично используются только в операционных системах или руководителях, которые используют механизм защиты уровни привилегий. Точки входа по вызову также полезны для передачи контроля за программой между 16-битовыми и 32-битовыми сегментами кода, как описано в Главе 16.4, “Передача Контроля Между Сегментами кода Смешанного размера.” Рисунок 4-8 показывает формат дескриптора точек входа по вызову в процедуру . Это дескрипторы точк входа по вызову которые могут находиться в GDT или в LDT, но не в таблице дескрипторов прерываний (IDT). Они выполняет шесть функций:

  • Они определяют сегмент кода, к которому будет получен доступ.
  • Они определяют точки входа в процедуры в указанном сегменте кода.
  • Они определяют уровни привилегии, которые должен иметь вызывающей, который пытается получить доступ к процедуре.
  • Если происходит переключение стека, то определяет число дополнительных параметров, которые будут скопированы между стеками.
  • Они также определяют размер значений, которые будут помещаться в верхушку стека назначения: 16-битовые точки входа по вызова задают 16-битовые pushes, и 32-битовые точки входа по вызову задают 32-битовые pushes.
  • Они однозначно определяет является ли дескриптор точки входа по вызову в процедуру действительным.

Том 3 рис 4.8

Поле селектора сегмента в дескрипторе точки входа по вызова определяет сегмент кода, к которому будет получен доступ. Поле смещения определяет точку входа в сегменте кода. Эта точка входа к правило ссылается на первую инструкцию заданной процедуры. Поле DPL указывает уровень привилегии точки входа по вызову, которые в свою очередь являются уровнем привилегии, который должен быть установлен чтобы получить доступ к выбранной процедуре через точку входа. Флаг P указывает, является ли дескриптор точки входа по вызову действительным. (Присутствие сегмента кода, на который указывает точка входа, обозначено флагом P в дескрипторе сегмента кода.) Поле число параметров указывает число параметров, которые будут скопированы из стек вызываемой процедуры в новый стек, если переключение стека происходит (см. Главу 4.8.5, “Переключение Стека”). Число параметров определяет число слов(Word) для 16-битных точек входа по вызову и двойных слов (DWord) для 32-битных точек входа по вызову.


Примечание, что флаг P в дескрипторе точки входа обычно в нормальном состоянии всегда устанавливается в 1. Если он установлен в 0, то произойдет исключение о отсутствии (#NP) , когда программа пытается получить доступ к дескриптору. Операционная система может использовать флаг P для своих целей. Для примера он может использоваться, чтобы отследить сколько раз в течении некоторого времени, точки входа вызывались. Для этого, в качестве ловушки флаг P первоначально установлен в 0, это нужно что бы вызвать обработчик исключения о отсутствии. Обработчик исключения тогда увеличивает счетчик и устанавливает флаг P в 1, так, чтобы при возвращении от обработчика, что бы точка входа была действительна.


Точки входа по вызову в режиме IA-32e.[]

Дескрипторы точек по вызову в 32-битном режиме снабжены 32-битным смещением для указателя инструкций (EIP); 64-битные расширения удваивают размер 32-битных точек входа по вызову, чтобы сохранить 64-битный указатель инструкций (RIP). См. рисунок 4-9:

  • Первые восемь байтов (байты 7:0) 64-битовых точек по вызову подобны, но не идентичны устаревшим 32-битным точкам входа по вызову. Поле, число копируемых параметров, было удалено.
  • Байты 11:8 содержат верхние 32 бита смещения сегмента к которому передается управление в канонической форме. Исключение общей защиты (#GP) будет генерироваться, если программное обеспечение по пытается использовать точку входа по вызову со смещением которому передается управление, и при этом это смещение не будет в канонической форме.
  • 16-байтовые дескрипторы могут проживать в той же самой таблице дескрипторов вместе с 16-битными и 32-битными дескрипторами. Поле типа, используемое для проверки согласованности, находится в битах 12:8 самого высокого двойного слова (dword) 64-битового дескриптора (очищенный в ноль). Если будет сделана попытка получить доступ к верхней половине 64-битного дескриптора как к 32-битному дескриптору, то произойдет исключение общей защиты (#GP).

Том 3 рис 4.9

  • Сегменты кода, которому передается управление, на которые ссылается 64-битная точка входа по вызову, должен быть 64-битовыми кодовыми сегментами (CS.L = 1, CS.D = 0). В противном случае ссылка вызови генерацию исключение общей защиты, #GP (селектор CS).
  • Только 64-битные точки входа по вызову могут сылаться на режим IA-32e (64-битном режиме и режиме совместимости). У наследованных 32-битые точки входа по вызову в процедуру имеют тип 0CH переопределен в режиме IA-32e как 64-битовый тип точек входа по вызову, но 32-битный тип точек входа по вызову существует и в режиме IA-32e.
  • Если дальней вызов обращается по 16-битовой точке входа по вызову (04h) в режиме IA-32e, то будет сгенерировано исключение общей защиты (#GP).

Когда вызов обращается по 64-битному типу точки входа по вызову, предпринимаются действия идентичные таким как в 32-битном режиме, со следующими исключениями:

  • Вытеснение стека (pushs) будут делать восьми байтовые приращения стека.
  • 64-битовый RIP помещается в стек.
  • Копирование параметров, не выполняется.

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


Получение доступ к сегменту кода через точку входа по вызова в процедуру.[]

Для доступа к точке входа по вызову в процедуру, используют дальний указатель на точку входа как операнд места назначения в инструкциях CALL или JMP. Селектор сегмента от этого указателя определяет точку входа по вызову (см. рисунок 4-10); смещение от указателя требуется, но не используется или проверяется процессором. (Смещение может быть любым значением).

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

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

• CPL (текущий уровень привилегии).

• RPL (уровень привилегии запроса) селектора точки входа по вызову.

• DPL (дескрипторный уровень привилегии) дескриптора точки входа по вызову в процедуру.

• DPL дескриптора сегмента сегмента кода назначения.


Флаг C (приспособленец) в дескрипторе сегмента для сегмента кода приспособленца, также проверяется.

Том 3 рис 4.10

Том 3 рис 4.11

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


Таблица 4-1. Правила проверки привилегии для Точки входа по вызову.


Инструкция Правило
CALL CPL ≤ DPL точки входа по вызову; RPL ≤ DPL точки входа по вызову DPL сегмента кода назначения приспособленца ≤ CPLDPL сегмента кода назначения не приспособленца ≤ CPL
JMP CPL ≤ DPL точки входа по вызову; RPL ≤ DPL точки входа по вызову DPL сегмента кода назначения приспособленца ≤ CPLDPL сегмента кода назначения не приспособленца= CPL

Поле DPL дескриптора точки входа по вызову определяет в цифровой форме самый высокий уровень привилегии, имея который процедура запроса может получить доступ к точки входа по вызову ; то есть, чтобы получить доступ к точки входа по вызову, CPL процедуры запроса должен быть равным или меньше чем DPL точки входа по вызову. Например, см рисунок 4-15, у точки A входа по вызову DPL равен 3. Вызывающие процедуры которые имеют CPL (0 до 3) они все могут получить доступ к этой точке по вызову, это такие процедуры запроса которые располагаются в сегментах кода A, B, и C. Точка B входа по вызову имеет DPL 2, таким образом только запрос от процедуры с CPL или 0, 1, или 2 может получить доступ к точки B входа по вызову, такие процедуры запроса находятся в сегментах кода B и C. Пунктир показывает, что процедура запроса в сегменте кода A не может получить доступ к точки B входа по вызову.


RPL селектора сегмента точки входа по вызову должен удовлетворять тому же самому тесту что и CPL процедуры запроса; то есть, RPL должен быть меньше чем или равным DPL точки входа по вызову. В примере на рисунке 4-15 процедура запроса в сегменте кода C может получить доступ к точке B входу по вызову используя селекторы точек входа B2 или B1, но при этом не может использовать селектор точки входа B3, чтобы получить доступ к точке B входа по вызову.


Если проверки привилегии между процедурой запроса и точкой входа по вызову успешно пройдены, то процессор тогда сверяет DPL дескриптора сегмента кода с CPL процедуры запроса. Здесь, правила проверки привилегии различаются между инструкциями CALL и JMP. Только инструкции CALL могут использовать точки входа по вызову, чтобы передать контроль за программой к более привилегированному (в цифровой форме более низкому уровню привилегии) сегменту кода не приспособленца; то есть, к сегментам кода не приспособленцам с DPL меньше чем CPL. A инструкция JMP могут использовать точки входа по вызову только, чтобы передать контроль за программой кодовому сегменту не приспособленцу с равным DPL равным CPL. Инструкции CALL, и JMP могут обе передать контроль за программой более привилегированному сегменту кода приспособленца; то есть, к соответствовавшему сегменту кода с DPL меньше чем или равным CPL.


Если вызов сделан к более привилегированному (в цифровой форме более низкому уровню привилегии) сегмента кода не приспособленца назначения, CPL ниже чем DPL сегмента кода предназначения, и выполняется переключение стека (см. Главу 4.8.5, “Переключение Стека”). Если CALL или JMP сделаны к более привилегированному сегменту кода приспособленца назначения, CPL не изменяется, и никакого переключения стека не происходит.


Том 3 рис 4.12

Точки входа по вызову позволяют единственному сегменту кода иметь процедуры, к которым могут получить доступ с различных уровнях привилегии. Например, у операционной системы, расположенной в кодовом сегменте, могут быть некоторые услуги, которые предназначены, чтобы использоваться и операционной системой и прикладным программным обеспечением (таким как процедуры для того, чтобы работать с символьным вводом / выводом). Точки входа по вызова этих процедур могут быть настроены, так что позволят получить доступ на всех уровнях привилегий (от 0 до 3). Более привилегированные точки входа по вызову (имеющие DPL 0 или 1) могут быть настроены на другие услуги операционной системы, которые предназначены, чтобы использоваться только самой операционной системой (такие как процедуры, которые инициализируют драйверы устройств).

Смена стека[]

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

Каждая задача должна определить до 4 стеков: один для кода приложений (работающий на 3 уровне привилегий) и по одному на каждый из уровней привилегий 2, 1, и 0, которые намерены использоваться. (Если будут используются только два уровня привилегий [3 и 0], тогда достаточно определить только два стека.) Каждый из этих стеков распологается в отдельном сегменте и выбор стека определяются при помощи селектора сегмента.

Селектор сегмента и указатель вершины стека для стека 3 уровня привилегий расположены в регистрах SS и ESP, соответственно. Когда случается смена стека из кода 3 уровня привилегий, перед тем как начнётся выполнение процедуры, эти регистры будет автоматически сохранены в стеке вызванной процедуры.

Указатели на стеки с уровнями привилегий 0, 1, и 2 хоронятся в TSS для в настоящий момент выполняющейся задачи (см. рисунок 6.2.). Каждый из этих указателей состоит из селектора сегмента и указателя вершины стека (загруженного в регистр ESP). Это начальные значения указателя. Они используются строго только для чтения. Процессор не изменяет их, во время работы задачи. Они используются только, чтобы создать новый стек, когда выполняются вызовы к более привилегированному уровню (в цифровой форме к более низкому уровню привилегий). Процессор избавляется от этих стеков при возврате из вызванной процедуре. В следующий раз, когда процедура вызывается, то создается новый стек, используя начальный указатель вершины стека. (TSS не содержит определение для стека 3 уровня полномочий, это потому что процессор не позволяет выполнить передачу программного управления из процедуры, работающей с CPL 0, 1, или 2 к процедуре, работающей с CPL 3, за исключением возврата.)

Операционная система ответственна за создание стеков и описателей сегмента стека для всех уровней привилегий, которые будут использоваться и для того, чтобы инициировать указатели для этих стеков в TSS. Каждый стек должен быть доступным на чтение и запись (это определяется в поле типа в его описателе сегмента) и должен иметь достаточный размер (определяется в поле предела), чтобы содержать следующие элементы:

  • Место под содержимое регистров: SS, ESP, CS, и EIP - вызывающей процедуры.
  • Место для параметров и временных переменных требующихся вызванной процедуре.
  • Если выполнен неявный вызов к обработчику исключений или обработчику прерываний, то и место под регистр EFLAGS и код ошибки.

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

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

  1. Используется DPL целевого сегмента кода (новый CPL), чтобы выбрать указатель на новый стек (селектор сегмента и указатель вершины стека) из TSS.
  2. Читается селектор сегмента и указатель вершины стека из текущего TSS, для нового стека, на который будет выполнено переключение. Если будет обнаружены любые нарушения придела при чтении: селектор сегмента стека, указателя вершины стека, или описателя сегмента стека - то будет сгенирирована ошибка "недопустимый TSS" (#TS).
  3. Проверяется описатель сегмента стека на надлежащие привилегии и тип, если нарушения обнаружены ,то генерируется исключение недопустимый TSS (#TS).
  4. Временно сохраняется текущее значение регистров ESP и SS.
  5. Загружается селектор сегмента и указатель вершины стека для нового стека в регистры ESP и SS.
  6. Помещаются временно сохраненные значения регистров SS и ESP (для вызванной процедуры) в новый стек (см. рисунок 4.13).
  7. Копируются в новый стек параметры. Число параметров определено в поле "количество параметров" в точки входа по вызову, вызываемой процедуры. Если количество 0, то никакие параметры не копируются.
  8. В новый стек заносятся указатель на команд возврата (текущее значение регистров CS и EIP).
  9. Загружается селектор сегмента для нового сегмента кода и нового указателя команд из точки входа по вызову в регистры CS и EIP, соответственно, и начинается выполнение вызванной процедуры.

См. описание команды вызова(CALL) в Главе 3, список команд, в IA-32 руководстве разработчика программного обеспечения архитектуры Intel, том 2, для подробного описания проверок привилегий уровня и других проверок защиты, что процессор выполняет во время дальнего вызове через точку входа по вызову.

Рисунок 4.13. Стек, процесс переключающийся во время меж-уровненного вызова.

Поле количество параметров в точке входа по вызову определяет число число элементов данных (до 31), которые процессор должен скопировать от стека вызывающей процедуры в стека вызываемой процедуры. Если надо передать больше чем 31 элемент данных, то один из параметров может быть указателем на структуру данных, или воспользоваться сохраненным содержимым регистров SS и ESP для доступа к параметром лежащим в старом стеке. Размер элементов данных, которые передают в вызываемую процедуре, зависит от размера точки входа по вызову, как описано в Разделе 4.8.3, “точки входа по вызову.”

Смена стека в 64-битном режиме[]

Хотя правила проверки защиты для точки входа по вызову такие же как и в 32-разрядном режиме, смена стека в 64-разрядном режиме отличается.

Когда смена стека является частью 64-разрядного режима изменения уровня привилегий через точку входа по вызову, описатель нового SS (сегмент стека) не загружается; 64-разрядный режим только загружает RSP для нужного уровня из TSS. Новый SS принудительно обнуляется, а поле RPL селектора SS принудительно выставляется равному новому CPL. Новый SS, устанавливают в NULL, чтобы обработать вложенные дальние переходы (CALLF, INTn, прерывания и исключения). Старый SS и RSP сохраняются в новом стеке.

На последующем RETF старый SS извлекается из стека и загружается в регистр SS. См. Таблицу 4.2.

Таблица 4.2. Уровни стека в 64-разрядном режиме после CALLF с изменением CPL

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

В 64-разрядном режиме RETF позволяет загрузить ноль в SS при определенных условиях. Если целевой режим - 64-разрядный режим и целевой CPL <> 3, то IRET позволяет загрузить в SS нулевой селектор. Если сама вызванная процедура прервана,то НУЛЕВОЙ SS помещается в стековый фрейм. На последующем RETF НУЛЕВОЙ SS на стеке действует как флаг, чтобы сказать процессору: "не загружай новый описатель в SS".

Возврат из вызванной процедуры[]

Команда RET может использоваться, чтобы выполнить близкий возврат, дальний возврат в текущей уровень привилегий, и дальний возврат к отличающемуся уровню привилегий. Эта команда предназначена, чтобы выполнить возвращения из процедур, которые вызваны командой CALL. Она не поддерживает возврат от команды JMP, потому что команда JMP не сохраняет указатель команды возврата в стеке.

Ближний возврат только передает управление программы в пределах текущего сегмента кода; поэтому, процессор выполняет только проверку предела. Когда процессор выталкивает (pop) указатель на команду возврата из стека в регистр EIP, что приводит к проверке указателя, на не превышение предела текущего сегмента кода. Для дальнего возврата на текущей уровень привилегий, процессор выталкивает(pop) два значения из стека это: селектор сегмента для сегмента кода, куда возвращаемся и указатель на команду возврата. При нормальных условиях, эти указатели должны быть правильными, потому что они были помещены в стек командой CALL. Однако, процессор выполняет проверки привилегии, чтобы обнаружить ситуации: текущая процедура, возможно, изменила указатель, или была в не состоянии сохранить стек должным образом.

Дальний возврат, который требует смены уровня привилегий, разрешён только к менее привилегированному уровню (то есть, DPL сегмента кода возврата в цифровой форме больше чем CPL). Процессор использует поле RPL от значения регистра CS сохраненного при вызове процедуры (см. рисунок. 4.13), определять, требуется ли возврат к в цифровой форме более высокому уровню привилегий. Если RPL в цифровой форме больше (менее привилегированный) чем текущий уровень привилегий(CPL), то возврат между уровнями происходит.

Когда процессор выполняет дальний возврат к вызывающей процедуре, то делает следующие шаги, (см. рисунок 6.2. и 6.4. в Руководстве разработчика программного обеспечения архитектур IA-32 и Intel® 64 , Том 1, для иллюстрации содержания стека до и после возврата):

  1. Проверяет поле RPL сохраненного значения регистра CS, чтобы определить, требуется ли смена уровня привилегий при возврате.
  2. Загружает регистры CS и EIP значениями из стеке вызванной процедуры. (Проверяются тип и уровень привилегий в описателе сегмента кода, и RPL в селекторе сегмента кода.)
  3. (Если команда RET содержит операнд "число параметров", и возврат требует смены уровня привилегий, то..) Добавляется число параметров (в байтах, полученных от команды RET) к текущему значению регистра ESP (после того, как сделает выталкивание(pop) значений CS и EIP), затем вставляет параметры в стек вызывающей процедуры. Получающееся значение в регистре ESP указывает на сохраненную SS и значения ESP для стека вызывающей процедуры. (Замечание. Число байт в команде RET должно быть выбрано так, чтобы соответствовало числу параметров в точки входа по вызову, к которой обратилась вызывающая процедура, При оригинальном вызове число параметров умножается на их размер.)
  4. (Если возврат требует смены уровня привилегий, то..) Регистры SS и ESP загружаются значениями сохраненными и переключаются назад к стеку вызывающей процедуры. Значения SS и ESP для стека вызванной процедуры уничтожаются. Если будет обнаружено нарушение любого придела при загрузки селектора сегмента стека или указателя вершины стека, то будет сгенерировано исключение общей защиты (#GP). Новый описатель сегмента стека также проверяется на соответствие поля тип и нарушений полномочий.
  5. (Если команда RET содержит операнд число параметров, то..) Добавляется число параметров (в байтах, полученных от команды RET) к текущему значению регистра ESP, затем вставляются параметры в стек вызывающей процедуры. Получившееся значение ESP повторно не проверяется на предел сегмента стека. Если значение ESP - вне предела, то этот факт не будет распознан до следующей операции со стеком.
  6. (Если возврат требует смены уровня привилегий, то...) Проверяется содержимое сегментных регистров DS, ES, FS и GS. Если любой из этих регистров относится к сегментам DPL которых - меньше чем новый текущий уровень привилегий CPL(исключая приспосабливающийся сегментам кода), то такой регистр загружается нулевым селектором сегмента.

См. описание команды RET в Главе 4 Руководства разработчика программного обеспечения архитектур IA-32 и Intel® 64 , Том 2B, в котором детализировано описаны проверки уровня привилегий и другие проверки защиты, которые процессор выполняет при дальнем возврате.

Выполнение быстрых вызовов к системным процедурам при помощи команд SYSENTER и SYSEXIT[]

Команды SYSENTER и SYSEXIT были введены в архитектуру IA-32 в процессорах Pentium II ради обеспечения быстрого (с минимальными затратами) механизма для того, чтобы вызывать операционную систему или исполнительные процедуры. SYSENTER предназначен для использования пользовательским кодом, выполняющимся с уровнем привилегий 3, чтобы обратиться к операционной системе или исполнительным процедурам, выполняющихся с уровне привилегий 0. SYSEXIT предназначен для использования уровнем привилегий 0 операционных систем или исполнительными процедурами для быстрых возврата к уровню привилегий 3 пользовательских кода. SYSENTER может выполниться от уровней привилегий 3, 2, 1, или 0; SYSEXIT может только выполниться от уровня привилегий 0.

Команды SYSENTER И SYSEXIT – компаньонные команды, но они не составляют пару вызов/возврат. Это, потому что SYSENTER не сохраняет никакой информации о состоянии для использование SYSEXIT для возврата.

Целевая команда и указатель вершины стека для этих команд не определяются через операнды команды. Вместо этого они определены через параметры, введенные в MSR'ы и универсальные регистры.

Для SYSENTER, целевые поля задаются, используя следующие источники:

  • Целевой сегмент кода - читает из IA32_SYSENTER_CS.
  • Адрес целевой инструкции - читается из IA32_SYSENTER_EIP.
  • Сегмент стека - вычисляется, путём добавления 8 к значению из IA32_SYSENTER_CS.
  • Указатель вершины стека - Читает из IA32_SYSENTER_ESP.

Для SYSEXIT, целевые поля задаются, используя следующие источники:

  • Целевой сегмент кода - вычисляется, путём добавления 16 к значению из IA32_SYSENTER_CS.
  • Адрес целевой команды - читается из EDX.
  • Сегмент стека - вычисляется, путём добавления 24 к значению из IA32_SYSENTER_CS.
  • Указатель вершины стека - читает из ECX.

Команды SYSENTER И SYSEXIT выполняют "быстрые" вызовы и возвраты, потому что они принудительно переводят процессор на предопределенный уровень привилегий. На состояние с уровнем 0, когда SYSENTER выполняется и в состояние с предопределенным уровнем привилегий 3 , когда SYSEXIT выполняется. Благодаря принудительной предопределённости и непротиворечивости состояния процессора, число проверок привилегии значительно сокращается по сравнению с числом проверок выполняемых при дальнем вызове. Кроме того, предопределенное целевое состояния в MSR'ах и универсальных регистрах устраняет любое возможное исключение памяти при выборке инструкций из целевого кода Любое дополнительное состояние, которое должно быть сохранено, чтобы позволить вернуться к вызывающей процедуре, должно быть явно сохранено вызывающей процедурой или быть предопределенно через программные соглашения.

SYSENTER и Команды SYSEXIT в Режиме IA-32e[]

Для процессоров Intel 64, команды SYSENTER И SYSEXIT расширены, чтобы позволить быстрые системные вызовы от пользовательского кода, выполняющегося с уровне привилегий 3 (в режиме совместимости или 64-битовом режиме) к 64-битовым исполнительным процедурам, выполняющимся с уровне привилегий 0. IA32_SYSENTER_EIP MSR и IA32_SYSENTER_ESP MSR расширены, чтобы поддерживать 64-битовые адреса. Если режим IA-32e неактивен, то будут использоваться только более низкие 32-разрядные адреса, сохраненные в этих MSR'ах. Если 64-битовый режим активен, адреса, сохраненные в IA32_SYSENTER_EIP и IA32_SYSENTER_ESP должны быть каноническими. Заметьте, что в 64-битном режиме, IA32_SYSENTER_CS не должен содержать НУЛЕВОГО селектора.

Когда управление передаётся через SYSENTER,то следующие поля задаются и устанавливаются наборы битов:

  • Целевой сегмент кода - читается ненулевой селектор из IA32_SYSENTER_CS.
  • Изменяются атрибуты CS - устанавливается база CS = 0, и предел CS = FFFFFFFFH.
  • Адрес целевой команды - читает 64-битовый канонический адрес из IA32_SYSENTER_EIP.
  • Сегмент стека - вычисляется, путём добавления 8 к значению из IA32_SYSENTER_CS.
  • Указатель вершины стека - читает 64-битовый канонический адрес из IA32_SYSENTER_ESP.
  • Изменяются атрибуты SS - устанавливается база SS = 0, и предел SS = FFFFFFFFH.

Когда управление передаётся при помощи команды SYSEXIT к 64-битному коду пользовательского режима, используя REX.W, то следующие поля задаются и устанавливаются наборы битов:

  • Целевой сегмент кода - вычисляется, путём добавления 32 к значению из IA32_SYSENTER_CS.
  • Изменяется атрибут CS - L-бит = 1 (переход в 64-битный режим).
  • Адрес целевой команды - читается 64-битный канонический адрес из RDX.
  • Сегмент стека - вычисляется, путём добавления 40 к значению из IA32_SYSENTER_CS.
  • Указатель вершины стека - RSP обновляется, используя 64-битный канонического адрес из RCX.

Когда управление передаётся при помощи команды SYSEXIT к режиму совместимости, когда признак размера операнда - 32 бита, то следующие поля задаются и устанавливаются наборы битов:

  • Целевой сегмент кода - вычисляется, путём добавления 16 к значению из IA32_SYSENTER_CS.
  • Изменяет атрибут CS - L-бит = 0 (переход в режим совместимости).
  • Адрес целевой команды - 32-разрядный адрес читается из EDX.
  • Сегмент стека - вычисляется, путём добавления 24 к значению из IA32_SYSENTER_CS.
  • Указатель вершины стека - ESP обновляется из 32-разрядного адреса в ECX.

Быстрые системные вызовы в 64-битном режиме[]

Команды SYSCALL и SYSRET спроектированы для операционных систем, которые используют модель сплошной памяти (сегментация не используется). Командам, наряду с SYSENTER и SYSEXIT, подходят для работы в режиме IA-32e.

SYSCALL и SYSRET, однако, не работают в режиме совместимости. Используйте CPUID, чтобы проверить, доступны ли SYSCALL и SYSRET (CPUID.80000001H.EDX [бит 11] = 1). SYSCALL предназначен для использования пользовательским кодом, выполняющимся с уровнем привилегий 3, чтобы обратиться к операционной системе или исполнительным процедурам, выполняющимся м уровне привилегий 0. SYSRET предназначен для использования уровнем привилегий 0 в операционных системах или исполнительны процедурах для быстрых возврата к уровню привилегий 3 пользовательского кода.

Указатели вершины стеков для SYSCALL/SYSRET не определены через модельные специфические регистры (MSR). Сброс битов в RFLAGS программируется, а не фиксированный. SYSCALL/SYSRET сохраняют и восстанавливают регистр RFLAGS.

Для SYSCALL, процессор сохраняет RIP команды в RCX и получает уровень привилегий 0, адрес целевой команды и указатель вершины стека из:

  • Целевой сегмент кода - читает ненулевой селектор из IA32_STAR [47:32].
  • Адрес целевой команды - читает 64-битный канонический адрес из IA32_LSTAR.
  • Сегмент стека - вычисляется, путём добавления 8 к значению из IA32_STAR [47:32].
  • Системные флаги - процессор сохраняет младшую 32 битную часть RFLAGS в регистре R11. После использует маску, полученную из IA32_FMASK, чтобы выполнить логическую операцию AND с более низкими 32 битами RFLAGS. Результат сохраняется в EFLAG.

Когда управление передаётся SYSRET к 64-битноу коду пользовательского режима, используя REX.W, процессор получает уровень привилегий 3, адрес целевых команды и указатель вершины стека из:

  • Целевой сегмент кода - рассчитывается путем сложения 16 и значения из ненулевого селектора из IA32_STAR [63:48].
  • Адрес целевой команды - адрес копируется из RCX в RIP.
  • Сегмент стека - рассчитывается путем сложения 8 и значения из IA32_STAR [63:48].
  • EFLAGS - загружается из R11.

Когда управление передаётся SYSRET к 32-разрядному коду пользователя режима, используя 32-разрядный размер операнда, процессор получает уровень привилегий 3, адрес целевых команды и указатель вершины стека из:

  • Целевой сегмент кода - читает ненулевой селектор из IA32_STAR [63:48].
  • Адрес целевой команды - адрес копируется значение из ECX в EIP.
  • Сегмент стека - рассчитывается путем сложения 8 и значения из IA32_STAR [63:48].
  • EFLAGS - загружается из R11.

Ответственность ОС гарантировать правильность описателей в GDT/LDT соответствующих селекторах, используемых в SYSCALL/SYSRET (корректность базовых адресов, пределов, и значений атрибутов используемых командами). Любой адрес, записанный в IA32_LSTAR сначала проверяется WRMSR, чтобы гарантировать каноническую форму. Если адрес не является каноническим, то будет сгенерировано исключение #GP. См. рисунок 4.14. для понимания размещения IA32_STAR, IA32_LSTAR и IA32_FMASK.

Рисунок 4.14. Регистры MSR используемые в SYSCALL и SYSRET

Привилегированные команды[]

Некоторые из системных команд (называемые “привилегированные команды”) защищены от использования прикладными программами. Привилегированные инструкции управляют системными функциями(как то загрузка системных регистров). Они могут выполниться только, когда CPL равен 0 (высшие привилегии). Если одна из этих команд выполняется, когда CPL не 0, то будет сгенерировано исключение общей защиты (#GP). Далее идёт список этих самых системные команды, которым дают привилегию:

  • LGDT — загрузить регистр GDT (Load GDT).
  • LLDT — загрузить регистр LDT (Load LDT).
  • LTR — загрузить регистр задачи (Load task register).
  • LIDT — загрузить регистр IDT (Load IDT register).
  • MOV (регистры управления) — Загрузка или сохранение управляющих регистров (Load and store control registers).
  • LMSW — Загрузить слово состояние машины (Load machine status word).
  • CLTS — Clear task-switched flag in register CR0.
  • MOV (отладочные регистры) — загрузка или сохранение отладочных регистров.
  • INVD — Invalidate cache, without writeback.
  • WBINVD — Invalidate cache, with writeback.
  • INVLPG —Invalidate TLB entry.
  • HLT— остановить процессор (Halt processor).
  • RDMSR — прочитать модельно специфические регистры (Read Model-Specific Registers).
  • WRMSR — записать модельно специфические регистры (Write Model-Specific Registers).
  • RDPMC — прочитать счётчик монитора производительности (Read Performance-Monitoring Counter).
  • RDTSC — прочитать счётчик временной метки (Read Time-Stamp Counter).

Некоторые из привилегированных команд доступны только в более новых семействах процессоров Intel 64 и IA-32 (см. Раздел 17.12, “Новые команды в Pentium и более поздних процессорах IA-32”).

Флаги PCE и TSD в регистре CR4 (биты 4 и 2, соответственно) доступны через команды RDPMC и RDTSC, соответственно, выполняющиеся с любым CPL.

Проверки правильности указателя[]

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

  1. Проверка прав доступа, чтобы определить, совместим ли тип сегмента с методом его использование.
  2. Проверка прав на чтение и запись.
  3. Проверка смещение указателя на превышение придела сегмента.
  4. Проверка поставщика указателя на разрешение обращению к сегменту.
  5. Проверка выравнивания смещения.

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

Advertisement