5.1. Контроль предела и выравнивания
Контроль предела
Контроль пределя является частью механизма защиты на уровне сегментов, поэтому важную роль в контроле предела играет дескриптор сегмента.
![Формат дескриптора](descr.gif)
В проверке контроля предела участвуют следующие биты из дескриптора сегмента:
- бит 55 - бит гранулярности (Granularity, G);
- бит 54 - размер по умолчанию (Default Size, D) (для сегментов стека);
- бит 42 - расширение вниз (Expand-down, E) (для сегментов данных).
Когда бит гранулярности G сброшен, предел задается 20-битным значением в байтах и варьируется от 00000 до FFFFFh (1Мбайт). Когда бит гранулярности G установлен, процессор сдвигает 20-битное значение предела на 12 бит влево. В этом случае предел сегмента задается в 4Кбайтных страницах и варьируется от FFFh (предел равен 0) до FFFFFFFFh (предел равен FFFFFh). Именно в этом случае размер сегмента может достигать 4Гбайт. Когда выставлен бит гранулярности, процессор не подвергает контролю младшие 12 бит линейного адреса. Например, при пределе 0 смещения 00000000-00000FFFh не вызывают исключения.
Для всех типов сегментов, кроме сегментов данных, расширяемых вниз (E=1), предел - это максимальное смещение в сегменте, по которому допустимо чтение одного байта. Т.е. значение предела на 1 меньше размера сегмента. Нарушение общей защиты возникнет, если программа попытается прочитать:
- один байт по смещению, превышающему предел сегмента;
- одно 16-битное слово по смещению, превышающему предел сегмента - 1;
- одно двойное (32-битное) слово по смещению, превышающему предел сегмента - 3;
- одно четверное (64-битное) слово по смещению, превышающему предел сегмента - 7 и т. п.
В сегментах данных, расширяемых вниз, (обычно это сегменты стека) значение предела играет противоположную роль. Значение предела соответствует минимальному смещению, обращение по которому вызывает исключение. Таким образом, для 16-битных сегментов (D=0) допустимым диапазоном адресов является "предел+1" ... FFFFh, а для 32-битных сегментов (D=1) - "предел+1" ... FFFFFFFFh. Для таких сегментов размер максимален, когда предел равен 0.
Контроль предела позволяет легко выявить ошибки "убегания" кода, выхода за пределы массива, неверного вычисления указателей.
В дополнение к контролю пределов сегментов, процессор контролирует пределы дескрипторных таблиц (GDT, LDT, IDT) и сегментов состояния задач (TSS). Таким образом, программа не может обратиться к памяти по селектору сегмента, дескриптор которого не попадает в предел дескрипторной таблицы.
Контроль выравнивания
Возможность контроля выравнивания впервые появилась в МП Intel486. Контроль выравнивания применим только к ссылкам на сегменты данных (или стека). Контроль выравнивания заключается в следующих проверках:
- 16-битное слово (например, короткое целое, селектор сегмента) должно храниться по адресу, кратному 2;
- 32-битное (двойное) слово (например, целое число, вещественное число одинарной точности, 32-битный указатель) должно храниться по адресу, кратному 4;
- 48-битные дальние указатели и 48-битное содержимое регистров дескрипторных таблиц должны храниться по адресу, кратному 4;
- 64-битное (четверное) слово (например, вещественное число двойной точности), а также 80-битное вещественное число расширенной точности должны храниться по адресу, кратному 8;
- 128-битные данные для XMM (Pentium III+) должны храниться по адресу, кратному 16.
Процессор подвергает указатель контролю выравнивания только в том случае, если текущий уровень привилегий CPL=3, управляющий бит CR0.AM=1 и флаг EFLAGS.AC=1. Бит CR0.AM выставляется операционной системой в зависимости от того, поддерживает ли она такой вид контроля. Бит EFLAGS.AC выставляется прикладной задачей в зависимости от того, требуется ли ей такой вид контроля. При нарушении контроля выравнивания генерируется исключение #17.