В архитектуре, совместимой с 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 0000 0 111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1100
После установки it
в intArray
и увеличения it
один раз и с учетом sizeof(int) == 4
он станет следующим:
0000 0000 0000 0000 1 000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
47-й бит выделен жирным шрифтом. Здесь происходит то, что второй указатель, полученный из арифметики указателя, недействителен, потому что не в канонической форме. Правильный адрес должен быть:
1111 1111 1111 1111 1 000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
Как программы справляются с этим? Есть ли у гарантии, что вам никогда не будет выделена память, чей диапазон адресов не зависит от 47-го бита?