С++ компиляторам разрешено оптимизировать запись в память:
{
//all this code can be eliminated
char buffer[size];
std::fill_n( buffer, size, 0);
}
При работе с конфиденциальными данными типичный подход использует указатели volatile*
, чтобы гарантировать, что запись в память испускается компилятором. Здесь реализована функция SecureZeroMemory()
в библиотеке времени исполнения Visual С++ (WinNT.h):
FORCEINLINE PVOID RtlSecureZeroMemory(
__in_bcount(cnt) PVOID ptr, __in SIZE_T cnt )
{
volatile char *vptr = (volatile char *)ptr;
#if defined(_M_AMD64)
__stosb((PBYTE )((DWORD64)vptr), 0, cnt);
#else
while (cnt) {
*vptr = 0;
vptr++;
cnt--;
}
#endif
return ptr;
}
Функция передает пройденный указатель на указатель volatile*
, а затем записывает его. Однако, если я использую его для локальной переменной:
char buffer[size];
SecureZeroMemory( buffer, size );
сама переменная не volatile
. Таким образом, согласно С++. Стандартное определение наблюдаемого поведения пишет в buffer
не считается наблюдаемым поведением и выглядит так, будто его можно оптимизировать.
Теперь есть много комментариев ниже о файлах страниц, кешах и т.д., которые все допустимы, но пусть просто игнорирует их в этом вопросе. Единственный вопрос, о котором идет речь, заключается в том, оптимизирован ли код для записи в память или нет.
Можно ли гарантировать, что код, делающий запись в память, не оптимизирован в С++? Является ли решение в SecureZeroMemory()
совместимым со стандартом С++?