Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
мпс с 1 по 294.doc
Скачиваний:
13
Добавлен:
16.04.2019
Размер:
6.2 Mб
Скачать

2.7.3. Защита памяти

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

  • контроль доступа к сегментам и страницам, который обеспечивается описанной ниже системой привилегий;

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

  • ограничение набора выполняемых команд в зависимости от уровня привилегий выпол­няемой программы (выделение привилегированных команд).

Для защиты информации, хранящейся в сегментах памяти, используется система привилегий, которая регулирует доступ к тому или иному сегменту в зависимости от уровня его защищенности и от степени важности (привилегированности) запроса. В процессорах Р6, так же как в более ранних моделях 80286, 80386, 80486, Pentium, установлены четыре уровня привилегий PL (privilege level), которые задаются номерами от 0 до 3. Наиболее привилегированным является уровень с меньшим номером. Степень защищенности сег­мента также имеет четыре уровня, которые схематически представляются в виде вложен­ных колец защиты (рис. 2.48).

Соответствующие уровни защищенности иллюстрируются на примере сегментов профамм. Наименее защищенными (привилегированными) являются прикладные программы пользователя, для которых выделяется уровень с номером PL = 3. Уровни с номерами PL = 0,1,2 отводятся для системных программ (супервизора), которые можно разделить на три уровня в зависимости от требований к их защищенности. Наиболее защищенная часть - ядро операционной системы (ОС) имеет уровень PL = 0. В ядро входит часть ОС, обеспечивающая инициализацию работы, управление доступом к памяти, защиту и ряд других жизненно важных функций, нарушение которых полностью выводит систему из строя. Основная часть программ ОС (утилиты) должна иметь уровень PL = 1. К уровню PL = 2 обычно относят ряд служебных профамм ОС, например, драйверы внешних устройств, системы управления базами данных, специализированные подсистемы программирования и другие.

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

В соответствии с уровнями привилегий и защищенности установлены следующие правила доступа для сегментов программ и данных:

  • данные из сегмента, имеющего уровень защиты PL, могут быть выбраны программой, имеющей такой же или более высокий уровень привилегий;

  • сегмент программ (процедура), имеющий уровень защиты PL, может быть вызван программой, имеющей такой же или более низкий уровень привилегий; различные вари­ анты вызова сегментов программ описаны ниже.

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

Уровень привилегий дескриптора DPL задается битами 5, 6 в байте доступа дескриптора (см. рис. 2.43). Указывает уровень защищенности сегмента, т. е. номер кольца защиты, к которому он относится.

Уровень привилегий запроса RPL задается битами 0,1 селектора, загруженного в сегмент­ный регистр при обращении к соответствующему сегменту. Этот уровень устанавливается вы­полняемой программой, которая с помощью данного селектора обращается к памяти системы.

Текущий уровень привилегий CPL задается битами 0, 1 селектора, размещенного в регистре сегмента программ CS. Определяет уровень привилегий выполняемого в настоящий момент сегмента программы.

Напомним, что меньшее значение DPL, RPL, CPL соответствует более высокому уровню привилегий (защиты). Наиболее защищенный сегмент имеет значение DPL = 0, наименее защищенный - DPL = 3. Для наименее привилегированных программ пользователя CPL = 3, наиболее привилегированные программы ядра ОС имеют CPL = 0.

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

Обращение к сегментам программ и данных в защищенном режиме производится : с учетом описанной выше системы привилегий. При этом правила обращения зависят от ! типа сегмента.

Обращение к сегментам данных производится с помощью селекторов, загружаемых в регистры DS, ES, FS, GS. При обращении анализируются значения RPL селектора и CPL текущей программы. Эффективный уровень привилегий для запроса данных в этом случае определяется как максимальное из значений RPL и CPL. Обращение к запрашиваемому сег­менту данных разрешается, если его уровень защиты DPL >= max(RPL, CPL). Нарушение этого правила при обращении вызывает исключение типа #GP («нарушение общей защиты»).

Таким образом, задавая определенное значение RPL в селекторе, можно управлять доступом текущей программы к сегментам данных, имеющим различный уровень защиты. Например, при RPL - 0 доступ к данным будет определяться значением CPL текущей программы. Если задать RPL = 3, то при любом CPL программа может обратиться только к наименее защищенным сегментам данных, относящимся к кольцу 3.

Обращение к сегменту стека выполняется путем загрузки селектора в регистр SS. Этот селектор должен выбирать дескриптор сегмента с разрешенной записью, т. е. бит 1 в байте доступа должен иметь значение W = 1 (см. рис. 2.43, б). Значения RPL и DPL должны быть равны CPL Нарушение этих правил (обращение к сегменту, для которого W = 0; использование селекторов и дескрипторов, имеющих значения RPL и DPL, не равные CPL) приводит к исключению типа #GP. При обращении к дескриптору, имею­щему значение бита присутствия Р = 0 (отсутствующий сегмент) возникает исключение типа #SS («ошибка обращения к стеку»).

Обращение к сегментам программ (передача управления) реализуется при выполне­нии команд межсегментного перехода JMP, вызова подпрограммы CALL и возврата из под­программы RET, прерывания INT и возврата из прерывания IRET. Правила обращения к сегментам программ зависят от значения бита подчиненности С в байте доступа дескриптора (см. рис. 2.43, а).

Обращение к подчиненным сегментам, для которых установлено значение С = 1, до­пускается только из программ, имеющих такой же или более низкий уровень привилегий. Таким образом, в программах с текущим уровнем привилегий CPL могут выполняться меж­сегментные команды JMP, CALL с передачей управления подчиненному сегменту, имеюще­му DPL= < CPL. При такой передаче значение CPL сохраняется, т. е. сохраняется уровень привилегий исходной (вызывающей) программы.

Обычно бит подчиненности С = 1 устанавливается для сегментов, которые могут ис­пользоваться программами с различными уровнями привилегий, например для системных библиотек. При этом подчиненные сегменты должны размещаться в кольце защиты с номе­ром, соответствующим максимальному уровню привилегий (минимальному значению CPL) вызывающих программ.

Обращение к неподчиненным сегментам, имеющим значение С = 0, с помощью ко­манд JMP и CALL допускается только в случае, если уровень привилегий текущей програм­мы равен уровню защиты сегмента: CPL = DPL.

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

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

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

Для вызова более привилегированной программы команда CALL или JMP должна обратить­ся к хранящемуся в LDT дескриптору шлюза вызова, формат которого показан на рис. 2.49, а.

В байтах 2,3 дескриптора шлюза содержится селектор вызываемого сегмента программы, а байты 0, 1, 6, 7 задают относительный адрес команды, являющейся точкой входа в эту программу. Байт доступа 5 содержит бит присутствия Р и поле уровня привилегий дескриптора шлюза gDPL, которые имеют такое же назначение, как и в дескрипторах сегментов. Поле ТУРЕ, в байте доступа шлюза (табл. 2.59) содержит код 0100, если вызываемая программа написана для 16-разрядного процессора 80286, или код 1100, если программа написана для 32-разряд­ных процессоров 386,486, Pentium, P6. Пятибитовое поле WC в байте 4 указывает количество параметров, которые переносятся из стека, обслуживающего программы с текущим уровнем привилегий CPL, в стек, используемый программами с уровнем привилегий вызываемой про­граммы. Параметры представляют собой 32-разрядные слова (16-разрядные для процессо­ров 80286), число которых может составлять от 0 до 31.

При вызове программ через шлюз должны выполняться следующие правила.

  • Значения RPL селектора, вызывающего шлюз, и CPL текущей программы должны быть меньше или равны значению gDPL в байте доступа шлюза. Таким образом, дескриптор шлюза должен иметь такой же или меньший уровень привилегий, чем запрос и текущая программа: gDPL >= (RPL.CPL).

  • Значение gDPL шлюза вызова должно быть больше или равно уровню DPL вызываемого сегмента программ: gDPL >= DPL.

  • Вызываемый сегмент программ должен иметь такой же или более высокий уровень привилегий DPL, чем текущая программа: DPL <= CPL.

  • Последнее из перечисленных правил реализуется при выполнении команды CALL с обраще­нием к любым видам сегментов программ (подчиненным и неподчиненным) и команды JUMP,вызывающей подчиненный сегмент. Если команда JUMP осуществляет переход к неподчи­ненному сегменту программ, то необходимо выполнение правила DPL = CPL.Hpn вызове программ через шлюз должны выполняться следующие правила.

  • Значения RPL селектора, вызывающего шлюз, и CPL текущей программы должны быть меньше или равны значению gDPL в байте доступа шлюза. Таким образом, дескриптор шлюза должен иметь такой же или меньший уровень привилегий, чем запрос и текущая программа: gDPL >= (RPL.CPL).

  • Значение gDPL шлюза вызова должно быть больше или равно уровню DPL вызываемого сегмента программ: gDPL >= DPL.

  • Вызываемый сегмент программ должен иметь такой же или более высокий уровень привилегий DPL, чем текущая программа: DPL <= CPL.

  • Последнее из перечисленных правил реализуется при выполнении команды CALL с обраще­нием к любым видам сегментов программ (подчиненным и неподчиненным) и команды JUMP, вызывающей подчиненный сегмент. Если команда JUMP осуществляет переход к неподчинен­ному сегменту программ, то необходимо выполнение правила DPL = CPL.

Примеры реализации этих правил при различных комбинациях значений RPL, CPL, gDPL, DPL иллюстрируются на рис. 2.50.

Если эти правила выполняются, то после вызова дескриптора шлюза в сегментный ре­гистр CS загружается селектор - байты 3,2 этого дескриптора. Этот селектор выбирает из LDT дескриптор вызываемого сегмента программы. При этом младшие два бита (поле RPL) селектора игнорируются, а вместо них в регистр CS в качестве CPL заносится значение DPL из дескриптора вызываемого сегмента. В регистр EIP из дескриптора шлюза загружается относительный адрес входа в программу - байты 0,1,6,7 (для программ процессора 80286 в IP загружаются байты 0,1).

Если вызванная программа имеет другой (более высокий) уровень привилегий, чем те­кущая, то при выполнении команд JMP, CALL с использованием шлюза вызова производит­ся переключение стека. Выполняется обращение к стеку, организованному для программ с уровнем привилегий, соответствующим уровню вызванной программы. В этот стек после­довательно вводятся старые значения SS и ESP; параметры, переносимые из старого стека; старые значения CS и EIP. Число переносимых параметров определяется полем WC (см. рис. 2.49), причем выбираются последние из загруженных в старый стек параметров. Последующие ячейки стека используются для хранения новых параметров. Переключение стека производится автоматически путем загрузки в регистры SS и ESP из сегмента состояния задачи TSS нового содержимого, которое определяет начальный адрес стека для программ данного уровня привилегий.

Необходимо отметить, что переключение стека реализуется только при обращении к не­подчиненным сегментам программ (имеющих в дескрипторе значение бита С = 0). Если че­рез шлюз осуществляется вызов подчиненного сегмента программ (значение бита С = 1), то переключение стека не производится.

По команде RET производится восстановление из стека старого содержимого регистров CS, EIP (IP) и SS, ESP (SP). Таким образом, одновременно с возвратом к исходной программе происходит, и возвращение к старому стеку. Команда RET проверяет значение CPL и два младших разряда извлекаемого из стека старого содержимого CS, определяющие уровень привилегий программы, к которой осуществляется возврат. Команда выполняется только в том случае, когда значение этих битов больше или равно CPL, т. е. возврат осуществляет­ся к программе с таким же или меньшим уровнем привилегий.

Нарушение командами JMP, CALL, RET указанных правил обращения к сегментам программ вызывает исключение типа #GP («нарушение защиты»).

Чтобы избежать частых нарушений хода выполнения программы из-за несоблюдения пра­вил доступа к сегментам, в набор команд процессора введены специальные команды, исполь­зуемые для проверки различных условий доступа. Команда LAR осуществляет загрузку в ре­гистр байта доступа сегмента, выбираемого с помощью селектора, который содержится в ад­ресуемой ячейке памяти или регистре. После этой операции процессор может произвести по­битный анализ содержимого байта доступа, определяя возможности обращения к данному сегменту. Команда LSL загружает в регистр значение границы сегмента, который выбирается таким же образом, как командой LAR. Используя полученное значение этой границы, процес­сор может организовать контроль формирования адреса, чтобы избежать прерывания про­граммы при выходе за пределы адресуемого сегмента. Команды VERR, VERW

проверяют

возможности записи в сегмент данных и чтения из сегмента программ, устанавливаемые соот­ветствующими битами дескрипторов. Если запись или чтение разрешены, то устанавливается значение признака ZF = 1 в регистре EFLAGS. Предварительный анализ значения этого при­знака позволяет избежать запрещенных видов обращения к данному сегменту, вызывающих прерывание текущей программы. Адресация необходимого сегмента при выполнении команд VERR, VERW производится таким же образом, как и командой LAR.

Реализация ввода-вывода с помощью команд IN, OUT, INS, OUTS в защищенном режиме производится с учетом CPL выполняемой программы. Величина CPL сравнивается со зна­чением поля IOPL в регистре EFLAG. Ввод/вывод производится только при выполнении условия CPL >= IOPL. Нарушение этого условия вызывает исключение типа #GP, если до­ступ к адресованному командой порту не разрешен специальной битовой картой ввода-вывода, задаваемой при решении отдельных задач.