Я запускаю некоторые обновления через Undefined Behavior Sanitizer. Дезинфицирующее средство выдает сообщение, которое я не совсем понимаю:
kalyna.cpp:1326:61: runtime error: load of address 0x0000016262c0 with insufficient space for an object of type 'const uint32_t'
0x0000016262c0: note: pointer points here
20 8b c1 1f a9 f7 f9 5c 53 c4 cf d2 2f 3f 52 be 84 ed 96 1b b8 7a b2 85 e0 96 7d 5d 70 ee 06 07
^
В рассматриваемом коде делается попытка усложнить атаки на кэш, касаясь адресов в диапазоне строки кэша. Линия 1326 — это строка с reinterpret_cast
:
// In KalynaTab namespace
uint64_t S[4][256] = {
...
};
...
// In library's namespace
const int cacheLineSize = GetCacheLineSize();
volatile uint32_t _u = 0;
uint32_t u = _u;
for (unsigned int i=0; i<256; i+=cacheLineSize)
u &= *reinterpret_cast<const uint32_t*>(KalynaTab::S+i);
Почему дезинфицирующее средство требует uint32_t u
не хватает места для uint32_t
?
Или, может быть, я правильно разбираю сообщение об ошибке? Это то, на что жалуется дезинфицирующее средство? Если я неправильно анализирую, то на что жалуется sanitzer?
$ lsb_release -a
LSB Version: :core-4.1-amd64:core-4.1-noarch
$ gcc --version
gcc (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1)
Идентификатор S
не конвертируется в указатель того типа, который, как вы думаете, делает. В результате ваша арифметика с указателями выкидывает вас путь вне диапазона ваших данных, и это лучше всего показывает пример:
#include <iostream>
#include <cstdint>
uint64_t S[4][256];
int main()
{
std::cout << static_cast<void*>(S+0) << '\n';
std::cout << static_cast<void*>(S+1) << '\n';
std::cout << static_cast<void*>(S+2) << '\n';
std::cout << static_cast<void*>(S+3) << '\n';
std::cout << '\n';
std::cout << static_cast<void*>(*S+0) << '\n';
std::cout << static_cast<void*>(*S+1) << '\n';
std::cout << static_cast<void*>(*S+2) << '\n';
std::cout << static_cast<void*>(*S+3) << '\n';
}
Выход (очевидно, зависит от платформы)
0x1000020b0
0x1000028b0
0x1000030b0
0x1000038b0
0x1000020b0
0x1000020b8
0x1000020c0
0x1000020c8
Обратите внимание на шаг первой последовательности чисел 0x800 в нижней строке. Это имеет смысл, поскольку каждая строка состоит из 0x100 записей по 8 байт каждая (элементы uint64_t). Тип указателя, используемого в арифметике указателя: uint64_t (*)[256]
,
Теперь обратите внимание на шаг второй последовательности, которая вглядывается только в S[0]
, Шаг составляет 8 байт, по одному на каждый слот. Тип преобразованного указателя в этом расчете uint64_t *
,
Короче говоря, ваша арифметика указателя предполагает S
превращается в uint64_t*
и это не так. Как и все преобразования массива в указатель, он преобразуется в элемент указатель на первый элемент, включая тип said-same. Тип элемента в массиве массивов: uint64_t[256]
, поэтому преобразованный тип указателя uint64_t (*)[256]
,
Других решений пока нет …