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

10.4.2. Селектори та дескриптори сегментів і сторінок

Селектор — це 16-розрядна структура, яка завантажується в сегментні регістри (рис. 10.6).

Рис. 10.6. Формат селектора сегмента

Селектор адресує не сам сегмент, а його дескриптор. Дескриптори розташовані в таблицях, причому в певний момент процесору доступні дві таблиці: глобальна (спільна для всіх процесів) і локальна — таблиця поточного процесу. 13 старших розрядів селектора є індексом у таблиці дескрипторів. Таким чином, кожна таб­лиця може містити 2,3=8192 дескриптори. Один розряд селектора (біт 2), позначений прапорцем ТІ, вказує на таблицю, де розташовано дескриптор: ТІ=0 вказує на GDT, ТІ=1 — на LDT. Інші 2 розряди селектора (біт 1, біт 0) задають рівень привілеїв (Requested Privilege Level, RPL), що використовує механізм захисту.

Дескриптор сегмента має 8-байтову структуру. На рис. 10.7 показано формат дескриптора сегмента, а в табл. 10.2 наведено значення його полів. Звернемо ува­гу на два найголовніших поля дескриптора: 32-розрядну базову адресу (base) і 20-розрядну межу сегмента (limit).

Рис. 10.7. Формат дескриптора сегмента

Базова адреса вказує на початок сегмента в пам'яті. На відміну від реального режиму роботи процесора, в захищеному режимі передбачено використання вір­туальної пам'яті. Коли було впроваджено 32-розрядну адресну шину і в дескрип­торі сегмента під базову адресу виділено 4 байти (2, 3, 4 і 7), тобто з'явилася можливість прямої адресації 4 Гбайт, обсяг фізичної пам'яті комп'ютера, побудо­ваного на процесорах х86, становив 1-2 Мбайт. Навіть і тепер, майже через 20 ро­ків, комп'ютери архітектури х86 не завжди мають 4 Гбайт фізичної пам'яті. Тому 32-розрядні адреси, які містять дескриптори, а також ті, що використовує проце­сор для адресації команд та їхніх операндів (які отримують додаванням 32-роз- рядного зміщення до 32-розрядної базової адреси відповідного сегмента), адресу­ють віртуальний адресний простір.

Ці адреси називають лінійними. Перетворення лінійної адреси на фізичну здійснюють по-різному, залежно від моделі використання пам'яті, зокрема від то­го, чи застосовується сторінкове керування.

Таблиця 10.2. Поля дескриптора сегмента

Номер байта

Ширина поля

Символьне

Призначення і вміст полів

в дескрипторі

(розряди)

позначення

0...1

16

limit_l

Молодші 16 розрядів 20-розрядного поля межі сегмента. Межа сегмента визначає його розмір у байтах або 4-кілобайтових сторінках, залежно від значення біта G

2...3

16

base_l

Молодші 16 розрядів 32-розрядного поля бази сегмента. База сегмента визначає лінійну адресу початку сегмента у пам'яті

4

8

base_2

Розряди 16...23 32-розрядного поля бази сегмента

5

8

AR

Байт захисту

6 (біти 0...3)

4

limit_2

Старші 4 розряди 20-розрядного поля межі сегмента

6 (біт 4)

1

U (User)

Біт користувача.

Не має спеціального призначення і використовується програмістом на його розсуд

6 (біт 5)

1

-

=0 — біт не використовується

Межа сегмента визначає його розмір. Якщо задіяні для цього 20 розрядів інтерпретувати як розмір у байтах (коли прапорець гранулярності G дорівнює 0), то максимальний розмір сегмента становитиме 1 Мбайт, а якщо у 4-кілобайтових сторінках (прапорець гранулярності G дорівнює 1) — 4 Гбайт.

Окрім прапорця гранулярності важливий також для інтерпретації адрес прапорець розрядності D. Якщо D = 0, використовуються 16-розрядні операнди і ре жими 16-розрядної адресації, а якщо D = 1 — 32-розрядні операнди і режими 32-розрядної адресації.

Поля та прапорці, які стосуються захисту сегмента, зведені у спеціальний байт дескриптора, що має назву байт захисту (позначається AR). На рис. 10.8 по­

казано формат байта захисту, а в табл. 10.3 наведено призначення полів.

Рис. 10.8. Формат байта захисту дескриптора сегмента

Таблиця 10.3. Поля байта захисту дескриптора сегмента

Номер біта

Символьне

Призначення і вміст полів

в байті AR

позначення

0

A (Accessed)

Біт доступу до сегмента. Встановлюється апаратно під час звернення до сегмента

1

R (Readable)

Для сегментів коду це біт доступу, який визначає право на читання:

=0 — читання із сегмента заборонено; =1 — читання із сегмента дозволено

W (Writable)

Для сегментів даних це біт доступу, який визначає

можливість модифікації даних: =0 — записування в сегмент заборонено; = 1 — записування в сегмент дозволено

Таблиця 10.2 (закінчення)

Номер байта в дескрипторі

Ширина поля (розряди)

Символьне позначення

Призначення і вміст полів

6 (біт 6)

1

D (Digit capacity)

Біт розрядності операндів і адрес. =0 — використовуються 16-розрядні операнди і режими 16-розрядної адресації

6 (біт 7)

1

G

(Granularity)

= 1 — використовуються 32-розрядпі операнди і режими 32-розрядпої адресації

Біт гранулярності. =0 — розмір сегмента задається значенням поля limit у байтах (максимальний розмір 1 Мбайт)

7

8

base_3

=1 — розмір сегмента задається значенням поля limit у 4-кілобайтових сторінках (максимальний розмір — 4 Гбайт) Старші 8 розрядів 32-розрядного поля бази сегмента

Таблиця 10.4. Значення поля типу сегмента

Таблиця 10.3 (закінчення)

Номер біта

Символьне

Призначення і вміст полів

в байті AR

позначення

2

С (Conforming)

Для сегментів коду це біт підпорядкованості: =0 — звичайний сегмент коду; =1 — підпорядкований сегмент коду

ED (Expand Down)

Для сегментів даних це біт розширення вниз, який дає можливість розрізняти сегменти даних і стека, а також визначає трактування поля limit: =0 — сегмент даних (зростає в бік старших адрес); =1 — сегмент стека (зростає в бік молодших адрес)

3

Ε (Execution)

Біт призначення, який визначає тип сегмента:

або I (Intending)

=0 — сегмент даних або стека; =1 — сегмент коду

4

S (Segment/System)

=0 — біт «системний», який вказує на те, що цей дескриптор описує системний об'єкт, який може бути чи не бути сегментом у пам'яті '=1 — біт «сегмент», який вказує, що цей дескриптор описує сегмент, тип якого і порядок використання уточнюються бітами I, C/ED, R/W

5...6

DPL (Descriptor

Рівень привілеїв сегмента, який визначає рівень

Privilege Level)

виконання від 0 до 3. (0 — найвищий рівень, який зазвичай використовується для ядра ОС, 3 — найнижчий рівень)

7

Ρ (Present)

Біт присутності:

=0 — сегмента в поточний момент в оперативній пам'яті немає;

=1 — сегмент у поточний момент знаходиться в оперативній пам'яті

Хоча чотири молодші розряди байта захисту називають полем типу сегмента (type_seg), тип сегмента задається не розрядами 0...3, а розрядами 1...4. Розряд 0 встановлюється у разі доступу до сегмента і може бути використаний операцій­ною системою під час реалізації алгоритмів керування віртуальною пам'яттю. Розряд 4 визначає об'єкт, який описує цей дескриптор: чи він є сегментом у пам'яті, чи спеціальним системним об'єктом (деякі системні об'єкти теж являють собою спеціальні сегменти у пам'яті — таблиці дескрипторів або сегменти стану задачі). Розряди 1...3 визначають, власне, тип сегмента і права доступу до нього. У табл. 10.4 проілюстровано сукупну «роботу» цих розрядів.

Біт S

Комбінація бітів у полі type_seg

Тип сегмента

0

0100

Таблиця локальних дескрипторів (LDT)

0

0001

Сегмент стану задачі (TSS)

1000

1101

1101

1

000х

Сегмент даних, тільки для читання

1

001х

Сегмент даних, дозволено читання і записування

1

010х

Не визначено

Таблиця 10.4 (закінчення)

Рис. 10.9. Формат дескриптора сторінки

Таблиця 10.5. Поля дескриптора сторінки

Окремі розділи таблиці сторінок може бути витіснено з фізичної пам'яті на диск. При цьому кожний розділ таблиці має розмір 4 Кбайт (210 = 1024 дескриптори) і відтак займає рівно одну сторінку. Таблиця розділів завжди присутня у пам'яті, її фізична адреса міститься в регістрі сгЗ.

Біт S

Комбінація бітів

Тип сегмента

у полі type_seg

1

011х

Сегмент стека, дозволено читання і записування

1

100х

Сегмент коду, дозволено лише виконання

1

101х

Сегмент коду, дозволено читання і записування

1

110х

Підпорядкований сегмент коду, дозволено лише виконання

1

111х

Підпорядкований сегмент коду, дозволено читання і виконання

Ще два розряди байта захисту дескриптора — 5 і 6 — визначають рівень привілеїв дескриптора (Descriptor Privilege Level, DPL). Разом із рівнем привілеїв селектора RPL і поточним рівнем привілеїв процесу, що виконується (Current Privilege Level, CPL), ці рівні дають змогу організувати розмежування доступу до сегментів за мандатним принципом (так звані кільця захисту) [91].

Якщо включено сторінковий механізм керування пам'яттю (біт pg регістра сг0 дорівнює 1), то з дескриптора визначається лінійна базова адреса сегмента, а після додавання до неї зміщення — лінійна адреса даних. Для обчислення фізичної адреси здійснюється сторінкове перетворення. Більш докладно цей процес буде розглянуто далі, а тепер звернемо увагу лише на пов'язані з ним структури даних.

Лінійна адреса — 32-розрядна, старші 20 розрядів інтерпретуються як номер сторінки, а молодші 12 — як зміщення у сторінці. Номер сторінки інтерпретується як індекс дескриптора сторінки, а з дескриптора визначається номер у фізичній пам'яті, який водночас становить старші 20 розрядів 32-розрядної фізичної базової адреси сторінки. На рис. 10.9 показано формат дескриптора сторінки, а в табл. 10.5 наведено значення його полів.

Номер

Символьне

Призначення і вміст полів

біта

позначення

0

Ρ (Present)

Прапорець наявності сторінки у фізичній пам'яті

1

W (Writable)

Прапорець дозволу записування у сторінку

2

U (User mode)

Прапорець користувач/супервізор

3

PWT

Керують механізмом кешування сторінок (введені

4

PCD

починаючи з процесора 80486)

5

A (Accessed)

Ознака того, чи було здійснено доступ до сторінки

6

D

Ознака модифікації вмісту сторінки

7...8

0

Зарезервовані

9...11

AVL (Available)

Зарезервовані для потреб операційної системи

12...31

Номер сторінки у пам'яті

10.5. Керування оперативною пам'яттю

Як уже зазначалося, архітектура процесорів х86 передбачає сегментну і сегментно-сторінкову моделі розподілу пам'яті. Засоби сегментної організації формують верхній рівень керування пам'яттю, а сторінкової — нижній (який можна вмикати та вимикати за допомогою прапорця pg в регістрі сг0) [63, 92].

10.5.1. Сегментний розподіл пам'яті

У сегментній моделі пам'яті кожний сегмент утворює свій окремий адресний простір (див. рис. 10.1). Кількість сегментів визначається максимальною кількі­стю дескрипторів, яку може обробити процесор. Як уже зазначалося, кожний процес використовує одночасно дві таблиці — LDT і GDT; максимальна кількість дескрипторів у кожній із них визначається розрядністю індексу з селектора — 13 розрядів. Тобто в кожній таблиці може міститися до 213 дескрипторів, загалом — 16k (16 384) сегментів. З цього випливає, що в такому режимі кожний процес мо­же мати 16k * 4 Гбайт = 64 Тбайт віртуального адресного простору.

Розглянемо механізм трансляції адреси, коли процесор працює в такому ре­жимі (рис. 10.10, 10.11).

Рис. 10.10. Механізм перетворення віртуальної адреси на фізичну, коли процесор х86 функціонує в сегментному режимі з використанням дескриптора сегмента з таблиці GDT

Рис. 10.11. Механізм перетворення віртуальної адреси на фізичну, коли процесор х86 функціонує в сегментному режимі з використанням дескриптора сегмента з таблиці LDT

Цей механізм ініціюється за спроби звернутися до будь-яких даних у пам'яті (наприклад, коли дані завантажуються в регістр процесора чи записуються з peгiстра в пам'ять, та під час роботи зі стеком) або до нового сегмента (завантаження селектора в сегментний регістр). Принципово цей механізм відповідає схемі, наве­деній на рис. 10.3. Насправді-ж перша частина алгоритму виконується лише під час завантаження селектора в сегментний регістр. Якщо здійснюється доступ до даних всередині сегмента, пошук дескриптора сегмента не виконується, позаяк процесор має спеціальні 64-розрядні регістри для зберігання дескрипторів, що відповідають завантаженим у сегментні регістри селекторам. Ці дескрипторні регістри є тіньови­ми, програмно недоступними. Дескриптор завантажується разом із селектором.

Завантаження селектора в сегментний регістр

Розглянемо процедуру завантаження селектора в сегментний регістр. Спочатку процесор має знайти у пам'яті відповідний дескриптор і здійснити необхідні перевірки.

Якщо у селекторі прапорець ТІ = 0, то дескриптор міститься в глобальній таблиці дескрипторів GDT. Базова адреса і межа (тобто розмір) таблиці GDT ви­значаються з регістра gdtr. Для вибору потрібного дескриптора використовується індекс, що міститься в селекторі. Індекс — це фактично номер дескриптора в таблиці. Оскільки адресація здійснюється побайтово, індекс необхідно помножи­ти на 8 (розмір дескриптора в байтах), що досягається зміщенням уліво на три розряди. Насправді ж, через те що індекс у селекторі займає 13 старших розрядів, він вже фактично зсунутий на три розряди вліво, тому достатньо обнулити три молодших розряди селектора і додати останній до базової адреси таблиці. Далі здійснюється перша операція, що стосується захисту сегментів пам'яті, — переві­ряється, чи не виходить дескриптор за встановлену межу таблиці GDT. Якщо ні — відбувається звернення до дескриптора. У разі виходу за межу генерується внутрішнє переривання процесу — виняткова ситуація 11.

Якщо ж у селекторі прапорець ТІ = 1, дескриптор міститься в таблиці LDT. Тоді спочатку визначається поточна таблиця LDT, для чого як селектор викори­стовують уміст регістра Idtr. Після виконання операції, аналогічної описаній вище, з таблиці GDT вибирається дескриптор, що описує таблицю LDT (нагадаємо, що таблиця дескрипторів — це спеціальний вид сегмента). При цьому перевіряється, чи відповідає тип дескриптора таблиці дескрипторів (див. табл. 10.4) та чи є таб­лиця в пам'яті (біт Ρ байта AR дескриптора дорівнює 1). З дескриптора таблиці LDT визначаються базова адреса таблиці та її межа. Потім із цієї таблиці оби­рається потрібний дескриптор, так само, як із таблиці GDT і з тими самими перевірками. (На рис. 10.10 і 10.11 операції перевірки виходу поза межі таблиць і сегментів не показано.)

В обох випадках було розглянуто процедуру до моменту звернення до деск­риптора сегмента, селектор якого ми завантажуємо в регістр.

Далі здійснюється перевірка сумісності типів селектора і дескриптора. Дес­криптор має описувати сегмент у пам'яті (біт S = 1). Якщо селектор завантажу­ється в регістр, що вказує на сегмент даних, то несумісним буде дескриптор, що описує сегмент коду із захистом від читання (біт Ε = 1, біт R = 0). Якщо селек­тор завантажується в регістр cs, то сумісним буде лише сегмент коду, і ана­логічно під час завантаження селектора в регістр ss сумісним буде лише сегмент стека (табл. 10.6). Очевидно, більш суворі умови сумісності для сегментів стека і коду спрямовані на запобігання «підсовування» процесору зловмисного коду через сегменти даних. У випадку коли перевірка дає негативний результат, фік­сується несумісність типів — генерується виняткова ситуація 13 (загальна по­милка захисту).

Таблиця 10.6. Дозволені комбінації бітів байта захисту дескриптора під час виконання операції завантаження селектора в сегментний регістр

Регістр

Ρ

DPL

S

Ε

C/ED

R/W

Α

Примітка

CS

χ

χ χ

1

1

χ

χ

χ

Сегмент коду

SS

χ

χ χ

1

0

1

1

χ

Сегмент стека

ds, es, fs, gs

χ

χ χ

1

1

χ

1

χ

Сегмент коду

0

χ

χ

χ

Сегмент даних або стека

Потім перевіряються привілеї доступу до сегмента. Для цього порівнюються значення RPL (рівень привілеїв селектора, який ми завантажуємо в сегментний регістр процесора), DPL (рівень привілеїв дескриптора, на який посилається се лектор, що ми його завантажуємо) і CPL (поточний рівень привілеїв, тобто рівень привілеїв процесу, що виконується), який дорівнює RPL селектора, що зна ходиться в регістрі cs). Для сегмента стека (за спроби завантажити селектор у регістр ss) має виконуватися рівність DPL = RPL = CPL. Для решти сегментів рівень DPLMae бути не вищим за рівні RPLi CPL, тобто DPL > RPL і DPL >CPL (па гадаємо, що найвищому рівню привілеїв відповідає значення 0, а найнижчому-3).

Якщо потрібне співвідношення не виконується, фіксується недостатній рівень

привілеїв — виняткова ситуація 13 (загальна помилка захисту).

Розглянемо приклад [91]. Зауважимо, що, описуючи послідовність перевірок, ми припускалися, що у разі фіксації помилки виконання алгоритму припиняється і викликається оброблювач відповідного переривання.

Приклад 1. Завантаження селектора в сегментний регістр

Виконується команда

mov. ds, ах

Регістр ах містить значення 0x37 = 0000000000110111. Процес виконується в нульовому кільці захисту, тобто CPL = 0.

  1. Аналізується біт ТІ селектора. ТІ = 1 (тобто дескриптор знаходиться в LDT).

  2. Обчислюється фізична адреса дескриптора dt локальної таблиці дескрипторів LDT, що відповідає поточному процесу (дескриптор dt знаходиться в таблиці GDT).

  3. З дескриптора dt добувається межа LDT ldt limit.

  4. Якщо в dt G=l, то ldt_limit *= 4096 (межа у 4-кілобітових сторінках).

  5. Із селектора добувається індекс дескриптора:

index = 6

  1. Перевіряється, чи не виходить індекс за межі таблиці LDT:

(index +1) * 8 - 1 = 55

Якщо ldt_limit < 55, фіксується некоректність селектора (помилка 11).

  1. З таблиці LDT добувається дескриптор d сегмента:

offset = index * 8 = 48; d_addr = ldt_base + offset

  1. Якщо в d біт S = 0 або одночасно Ε = 1 і R = 0, фіксується несумісність типів се­лектора і

регістра (помилка 13).

  1. Із селектора добувається RPL:

RPL = 3

  1. Порівнюються CPL, RPL і DPL:

CPL = 0, RPL = З

  1. Якщо DPL < 3, фіксується недостатній рівень привілеїв (помилка 13).

Селектор із регістра ах завантажується в регістр ds, тоді ж дескриптор d заван­тажується в дескрипторний регістр, що відповідає регістру ds.

Звернення до пам'яті

Під час звернення до пам'яті у разі її сегментного розподілу перевіряється, чи дозволено операцію та чи коректно здійснено доступ. Обмеження для команди зчитування з пам'яті встановлено лише для сегментів коду (біт Ε = 1): якщо біт R = 0 — читання заборонено. Для команди записування в пам'ять сегмент коду — взагалі несумісний тип, а всі інші типи сегментів можуть бути захищені бітом W: якщо W = 0, записування заборонено. В усіх згаданих випадках генерується ви­няткова ситуація 13 (загальна помилка захисту). У табл. 10.7 наведено дозволені комбінації бітів байта захисту.

Таблиця 10.7. Дозволені комбінації бітів байта захисту під час виконання операцій звернення до пам'яті

Операція

Ρ

DPL

S

Ε

C/ED

R/ W

Α

Примітка

Зчитування з пам'яті

1

1

Χ χ

χ χ

1

1

0

1

χ

χ

χ

1

χ

χ

Сегмент даних або стека Сегмент коду

Записування в пам'ять

1

χ χ

1

0

χ

1

χ

Сегмент даних або стека

Щоб перевірити коректність доступу, перевіряється, чи не виходить адреса за межу сегмента. Перевірка здійснюється з урахуванням розміру даних і напрямку зростання сегмента. Якщо сегмент є сегментом стека (біт Ε = 0 та біт ED = 1), то він зростає в бік молодших адрес, і тоді обчислена адреса має бути не меншою за межу сегмента. Якщо ж сегмент є сегментом коду (біт Ε = 1) або даних (біт Ε = 0 та біт ED = 0), він зростає в бік старших адрес, тому до обчисленої адреси додається розмір даних (для команди mov — залежно від того, який із регістрів процесора бере участь у передаванні-прийманні даних) і отримана адреса має бути не більшою за межу сегмента. За наявності помилки генерується виняткова ситуація 13 (загальна помилка захисту). Розглянемо такий приклад [91].

Приклад 2. Звернення до пам'яті

Виконується команда mov es:[ebx+4],еах

  1. Перевіряється біт Ε дескриптора d, завантажений у дескрипторний регістр, який відповідає сегментному регістру es. Якщо Ε = 1, фіксується спроба запису в сегмент коду (помилка 13).

  2. Перевіряється біт W дескриптора d. Якщо W = 1, фіксується спроба запису в сегмент, захищений від запису (помилка 13).

  3. Із сегмента d добувається межа сегмента seg_limit. Якщо в d значення G=l, то seg_limit *= 4096 (межа у 4-кілобайтових сторінках).

  4. Обчислюється зміщення: offset = ebx + 4

6. Перевіряється відсутність виходу за межу сегмента за таким алгоритмом: Якщо ED = 0 (сегмент даних зростає в бік старших адрес), то порівнюється offset + data_size — 1 = offset + 3 (зміщення останнього байта даних, розмір даних — 4 байти) і seg_limit; якщо offset + 3 > seg_limit, фіксується некоректне звернення (помилка 13).

Інакше, якщо ED = 1 (сегмент стека зростає в бік молодших адрес), то порівнюється offset і seg_limit; якщо offset < seg_limit, фіксується некоректне звернення (помилка 13).

7. З дескриптора d добувається seg_base.

8. Обчислюється адреса seg_base + offset, і в пам'ять за цією адресою заносяться дані з регістра еах.