Адресная каноническая форма и арифметика указателей
На архитектурах, совместимых с AMD64, адреса должны быть в канонической форме перед разыменовкой.
ОтРуководство Intel, раздел 3.3.7.1:
В 64-битном режиме адрес считается в канонической форме, если биты адреса от 63 до наиболее значимого реализованного бита микроархитектурой установлены на все единицы или все нули.
Теперь наиболее значимым реализованным битом в современных операционных системах и архитектурах является 47-й бит. Это оставляет нам 48-битное адресное пространство.
Особенно когдаASLR включен, пользовательские программы могут ожидать получения адреса с установленным 47-м битом.
Если используются такие оптимизации, как тегирование указателя, а верхние биты используются для хранения информации, программа должна убедиться, что 48-й и 63-й биты установлены обратно в 47-й бит перед разыменованием адреса.
Но рассмотрим этот код:
int main()
{
int* intArray = new int[100];
int* it = intArray;
// Fill the array with any value.
for (int i = 0; i < 100; i++)
{
*it = 20;
it++;
}
delete [] intArray;
return 0;
}
Теперь посмотрим, чтоintArray
есть, сказать:
0000 0000 0000 00000111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1100
После настройкиit
вintArray
и увеличиваетсяit
один раз, и учитываяsizeof(int) == 4
, это станет:
0000 0000 0000 00001000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
47-й бит выделен жирным шрифтом. Здесь происходит то, что второй указатель, полученный с помощью арифметики указателей, недопустим, потому что не в канонической форме. Правильный адрес должен быть:
1111 1111 1111 11111000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
Как программы справляются с этим? Есть ли гарантия у ОС, что вам никогда не будет выделена память, диапазон адресов которой не меняется на 47 бит?