Предыдущий раздел | РАБОТА С ОПЕРАТИВНОЙ ПАМЯТЬЮ В ЗАЩИЩЕННОМ РЕЖИМЕ | Следующий раздел |
Защита информации является необходимым условием функционирования мультипрограммной системы. В любой ВС информация находится в ОП и на ПУ. Так как оперативная память распределяется между процессами в виде сегментов, то требуется исключить воздействие процессов на сегменты памяти, не принадлежащие им. Более того, основной целью сегментного управления памятью как раз и является защита информации в ОП.
От кого должен быть защищен сегмент нашей программы? От чужих сегментов кода и, иногда, от своих. Основной принцип защиты следующий. Существует ряд ограничений, при нарушении любого из которых возникает исключение (внутреннее аппаратное прерывание), обработчик которого прекращает выполнение текущей программы, то есть уничтожает соответствующий программный процесс. Наиболее широко используется исключение № 0Dh – общее нарушение защиты (GP). Перечислим ограничения, нарушения которых приводят к исключениям.
1. Ограничение числа таблиц дескрипторов сегментов, доступных процессу. Любой прикладной или системный обрабатывающий процесс имеет доступ только к двум таблицам: GDT (она доступна всем процессам) и к своей LDT.
Рис. 79. Возможное распределение программ ВС по кольцам защиты
Несмотря на то, что в GDT находятся дескрипторы для всех LDT, существующих в данный момент времени в системе, выполнить загрузку адреса «чужой» LDT в LDTR текущий процесс не сможет из-за ограничения 6. Поэтому сегменты, описанные в «чужих» LDT, для процесса недоступны.
2. Ограничение числа доступных сегментов данных. Доступность для прикладного процесса таблицы GDT вовсе не означает доступность сегментов, дескрипторы которых находятся в GDT. Это объясняется разницей запрашиваемого и имеющегося уровней привилегий. Уровень привилегийPL - целое число от 0 до 3. 0 – самый привилегированный уровень, 3 – самый непривилегированный уровень. Эти уровни можно наглядно изобразить в виде колец защиты (рис.40). На этом рисунке показано, как могут быть распределены по кольцам защиты прикладные и системные программы ВС. В UNIX-системах из четырех уровней привилегий используются только два – 0 и 3. Уровень 0 присущ процессу в состоянии «Ядро», а уровень 3 – в состоянии «Задача».
Основной принцип ограничения числа доступных сегментов данных следующий. Любой сегмент кода, выполняемый на ЦП, имеет свой уровень привилегий CPL. В ходе этого выполнения обычно требуется использовать ячейки с данными, принадлежащие другим сегментам, каждый из которых также имеет свой уровень привилегий - DPL. Подобное использование возможно только в том случае, если выполняется условие: CPL <DPL. Иначе возникает исключение, обработчик которого прекращает выполнение процесса, содержащего сегмент кода. Например, сегменты из ядра ОС могут обращаться к любой ячейке любого сегмента из GDT или LDT, так как CPL=0. Теперь рассмотрим вопрос о том, где хранятся уровни привилегий.
Подпрограмма ядра ОС, выполняющая системный вызов СОЗДАТЬ_ПРОЦЕСС, создает для нового процесса таблицу LDT и помещает дескриптор этой таблицы в GDT. При этом в поле DPL (см. рис.33) каждого дескриптора в LDT помещается уровень привилегий соответствующего сегмента. Допустим теперь, что в результате диспетчеризации новый процесс переводится из состояния «Готов» в состояние «Задача». Иными словами программа процесса начинает выполняться на ЦП. Подобная передача управления программе производится загрузкой в регистр CS индекса дескриптора сегмента кода в таблице LDT. Кроме того, в качестве поля RPL в CS (см. рис.32) копируется DPL из дескриптора сегмента кода. Теперь до тех пор, пока выполняются машинные команды из этого сегмента кода, его RPL является текущим уровнем привилегий CPL. Следует отметить, что прикладной процесс не может изменить свой CPL, то есть его программа не может выполнить запись в поле RPL регистра CS. Но прочитать это поле программа всегда может.
Теперь допустим, что сегмент кода нашего процесса хочет обрабатывать какие-то ячейки из какого-то сегмента данных. Тогда одна из команд должна загрузить индекс, соответствующий положению в LDT или в GDT дескриптора этого сегмента, в соответствующий сегментный регистр данных, например, в DS. Что касается поля RPL (см. рис.35) в этом регистре, то записываемое туда значение называется уровнем запрашиваемых привилегий. Команда загрузки в сегментный регистр данных (DS, ES и т.д.) индекса и RPL завершится успешно только в том случае, если выполняется условие:
(DPL)в дескрипторе сегмента данных > max [(CPL)в CS , (RPL)в DS]
При этом очевидно, что запрашивать уровень привилегий лучше, чем CPL, бесполезно. Если указанное условие не выполняется, то загрузка в DS (и выполнение всей программы) завершится исключением GP.
3. Подобно тому, как процессу в состоянии «Задача» доступны не все сегменты данных, перечисленные в GDT, ему доступны и далеко не все сегменты кода. Иначе бесконтрольные вызовы подпрограмм ядра ОС могут привести к разрушению, как самой ОС, так и к разрушению прикладных программ.
С другой стороны, полный запрет вызова подпрограмм ядра прикладной программой также не допустим, так как программа должна иметь возможность обращаться к ОС с целью получения помощи от нее. Для этого прикладной программе должны быть доступны те подпрограммы ядра ОС, которые специально предназначены для обслуживания прикладных программ. Так как процесс в состояниях «Задача» и «Ядро» использует разные сегменты кода, то, во-первых, межсегментные переходы обязательно должны существовать, а во-вторых, эти переходы должны быть ограничены.
4. Ограничение длины доступных сегментов. После того, как загрузка сегментного регистра завершилась успешно, процесс (а точнее – его сегмент кода) получает доступ к ячейкам этого сегмента. Для адресации искомой ячейки при этом используется регистр-смещение. Меняя его содержимое, можно обрабатывать различные ячейки сегмента.
Как показано на рис.36, одно из полей дескриптора сегмента содержит предел - размер соответствующего сегмента. Попытка использовать в текущей команде адрес-смещение, превышающее предел сегмента, неизбежно приведет к исключению.
5. Ограничение формы доступа к сегменту. Основные формы доступа: «чтение», «запись» и «выполнение». Для сегментов данных возможными формами доступа являются «чтение» и «запись», а для сегмента кода – «выполнение» и «чтение».
Для конкретного сегмента могут быть разрешены не все возможные формы доступа. Для этого в дескрипторе сегмента кода (см. рис.34) бит R разрешает (R=1) или запрещает (R=0) чтение из сегмента кода. В дескрипторе сегмента данных (см. рис.38) бит W разрешает (W=1) или запрещает (W=0) запись в сегмент данных.
6. Ограничение множества допустимых машинных команд. Находясь в сегменте кода с уровнем привилегий CPL, мы можем выполнять только такие команды, которые соответствуют этому уровню.
В частности, при CPL=0 допустимы все команды используемого ЦП. Некоторые из этих команд разрешены только в этом приоритетном кольце. Они называются привилегированными командами. Перечислим их:
lgdt – загрузка GDTR;
lldt – загрузка LDTR;
lidt – загрузка регистра таблицы дескрипторов прерываний IDTR (рассматривается в п.);
ltr – загрузка регистра задачи TR (рассматривается в п.);
lmsw – загрузка регистра состояния машины (это 16 младших битов регистра CR0);
clts – сброс флага переключения задачи (рассматривается в п.);
hlt – останов ЦП.
Кроме привилегированных команд выполнение некоторых других команд также ограничено. Сюда относятся команды ввода-вывода и команды для работы с флагом разрешения прерываний IF. Контроль над этим флагом очень важен, так как программа, сбросившая его и вошедшая в бесконечный цикл, в принципе не может быть снята с ЦП никаким способом кроме перезагрузки ОС. Это обусловлено тем, что завершение процесса с зациклившейся программой выполняет или обработчик прерываний клавиатуры (при нажатии специальной комбинации клавиш), или это делает обработчик прерываний таймера (по истечению кванта времени процесса). Так как прерывания обоих этих типов являются маскируемыми, то при IF=0 они запрещены.
Изменение флага IF выполняют две команды: sti и cli. Их применение в программе процесса допустимо при выполнении условия: CPL<IOPL, где IOPL – число в битах 12 и 13 регистра флагов EFLAGS. Программы, работающие не в нулевом кольце, не могут модифицировать поле IOPL. Поэтому при выполнении команд, загружающих EFLAGS (команды iret и popf) не в нулевом кольце, поле IOPL не модифицируется. При этом также не меняется флаг IF. Команды iret и popf являются примерами чувствительных команд, функции которых зависят от текущего уровня привилегий процесса.
7. Ограничение доступа к периферийным устройствам. Доступ к ПУ программа осуществляет через порты, используя команды in, out, ins и outs. Контроль над использованием этих команд важен не только для зашиты информации, расположенной на ПУ (например, на диске), но и необходим для защиты информации в ОП. Это обусловлено тем, что с помощью этих команд можно, например, перепрограммировать контроллеры прерываний и прямого доступа в память (ПДП), что откроет путь к непосредственной модификации содержимого ОП по реальным адресам.
Ограничение использования команд ввода-вывода основано на совместном использовании упомянутого выше поля IOPL в регистре флагов EFLAGS и битовой карты ввода-вывода. Битовая карта ввода-вывода – битовая строка, в которой каждый бит соответствует одному порту ввода-вывода. Причем номер бита в карте равен номеру (адресу) порта. Каждому процессу назначается своя карта ввода-вывода.
Если CPL<IOPL, то на операции ввода-вывода не накладывается никаких ограничений. Если CPL>IOPL, то ЦП проверяет тот бит в карте ввода-вывода процесса, который соответствует адресуемому порту. Если этот бит равен 0, то текущая команда ввода-вывода выполняется. Иначе возникает исключение. Оно возникает и в том случае, если адресуемому порту не соответствует никакой бит в карте ввода-вывода (из-за ее короткой длины).
Предыдущий раздел | В начало | Следующий раздел |