Мені потрібна функція, яка (наприклад, SecureZeroMemory з WinAPI) завжди нульову пам’ять і не оптимізується, навіть якщо компілятор вважає, що пам’ять після цього більше ніколи не буде доступна. Здається, ідеальний кандидат на нестабільність. Але у мене є деякі проблеми, насправді змушуючи це працювати з GCC. Ось приклад функції:
void volatileZeroMemory(volatile void* ptr, unsigned long long size)
{
volatile unsigned char* bytePtr = (volatile unsigned char*)ptr;
while (size--)
{
*bytePtr++ = 0;
}
}
Досить просто. Але код, який GCC насправді генерує, якщо ви його викликаєте, сильно відрізняється залежно від версії компілятора та кількості байтів, які ви насправді намагаєтесь обнулити. https://godbolt.org/g/cMaQm2
- GCC 4.4.7 та 4.5.3 ніколи не ігнорує нестабільність.
- GCC 4.6.4 та 4.7.3 ігнорують нестабільність для розмірів масивів 1, 2 та 4.
- GCC 4.8.1 до 4.9.2 ігнорує мінливість для розмірів масивів 1 і 2.
- GCC 5.1 до 5.3 ігнорувати летючі для розмірів масивів 1, 2, 4, 8.
- GCC 6.1 просто ігнорує його для будь-якого розміру масиву (бонусні бали за послідовність).
Будь-який інший перевірений мною компілятор (clang, icc, vc) генерує сховища, які можна очікувати, з будь-якою версією компілятора та будь-яким розміром масиву. Отже, на даний момент мені цікаво, чи це (досить стара і серйозна?) Помилка компілятора GCC, чи визначення нестабільного у стандарті неточне, що це насправді відповідає поведінці, що робить по суті неможливим написання портативного " Функція SecureZeroMemory "?
Редагувати: Кілька цікавих спостережень.
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <atomic>
void callMeMaybe(char* buf);
void volatileZeroMemory(volatile void* ptr, std::size_t size)
{
for (auto bytePtr = static_cast<volatile std::uint8_t*>(ptr); size-- > 0; )
{
*bytePtr++ = 0;
}
//std::atomic_thread_fence(std::memory_order_release);
}
std::size_t foo()
{
char arr[8];
callMeMaybe(arr);
volatileZeroMemory(arr, sizeof arr);
return sizeof arr;
}
Можливий запис із callMeMaybe () змусить усі версії GCC, крім 6.1, генерувати очікувані сховища. Коментування в паркані пам'яті також змусить GCC 6.1 генерувати сховища, хоча лише в поєднанні з можливим записом із callMeMaybe ().
Хтось також запропонував очистити кеші. Microsoft взагалі не намагається очистити кеш-пам’ять у "SecureZeroMemory". Кеш-пам'ять, імовірно, буде втрачено чинність досить швидко, тому це, мабуть, не є великою проблемою. Крім того, якщо інша програма намагалася перевірити дані або якщо вона збиралася бути записана у файл сторінки, це завжди була б обнулена версія.
Є також деякі занепокоєння щодо GCC 6.1 із використанням memset () у автономній функції. Компілятор GCC 6.1 на godbolt може зірватися, оскільки GCC 6.1, здається, генерує звичайний цикл (як 5.3 робить на godbolt) для автономної функції для деяких людей. (Прочитайте коментарі відповіді zwol.)
volatile
- це помилка, якщо не доведено інше. Але швидше за все помилка.volatile
є настільки невизначеним, що є небезпечним - просто не використовуйте його.