Potrzebuję funkcji, która (jak SecureZeroMemory z WinAPI) zawsze zeruje pamięć i nie jest zoptymalizowana, nawet jeśli kompilator uważa, że pamięć nie będzie już nigdy dostępna. Wydaje się, że jest to idealny kandydat na niestabilność. Ale mam pewne problemy z uruchomieniem tego z GCC. Oto przykładowa funkcja:
void volatileZeroMemory(volatile void* ptr, unsigned long long size)
{
volatile unsigned char* bytePtr = (volatile unsigned char*)ptr;
while (size--)
{
*bytePtr++ = 0;
}
}
Wystarczająco proste. Ale kod, który faktycznie generuje GCC, jeśli go wywołasz, różni się gwałtownie w zależności od wersji kompilatora i ilości bajtów, które faktycznie próbujesz wyzerować. https://godbolt.org/g/cMaQm2
- GCC 4.4.7 i 4.5.3 nigdy nie ignorują zmienności.
- GCC 4.6.4 i 4.7.3 ignorują zmienne dla tablic o rozmiarach 1, 2 i 4.
- GCC 4.8.1 do 4.9.2 ignoruje zmienne dla tablic o rozmiarach 1 i 2.
- GCC 5.1 do 5.3 ignoruje volatile dla tablic o rozmiarach 1, 2, 4, 8.
- GCC 6.1 po prostu ignoruje to dla dowolnego rozmiaru tablicy (dodatkowe punkty za spójność).
Każdy inny testowany przeze mnie kompilator (clang, icc, vc) generuje sklepy, których można by się spodziewać, z dowolną wersją kompilatora i dowolnym rozmiarem tablicy. W tym miejscu zastanawiam się, czy jest to (dość stary i poważny?) Błąd kompilatora GCC, czy też definicja niestabilności w standardzie jest tak nieprecyzyjna, że jest to faktycznie zgodne zachowanie, co zasadniczo uniemożliwia napisanie przenośnego. „SecureZeroMemory”?
Edycja: kilka interesujących obserwacji.
#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;
}
Możliwy zapis z callMeMaybe () spowoduje, że wszystkie wersje GCC z wyjątkiem 6.1 wygenerują oczekiwane magazyny. Komentowanie w obszarze pamięci spowoduje również, że GCC 6.1 wygeneruje magazyny, chociaż tylko w połączeniu z możliwym zapisem z callMeMaybe ().
Ktoś zasugerował również opróżnienie pamięci podręcznych. Firma Microsoft w ogóle nie próbuje opróżnić pamięci podręcznej w „SecureZeroMemory”. Pamięć podręczna prawdopodobnie i tak zostanie unieważniona dość szybko, więc prawdopodobnie nie będzie to wielka sprawa. Ponadto, jeśli inny program próbował sondować dane lub miał zostać zapisany w pliku stronicowania, zawsze będzie to wersja wyzerowana.
Istnieją również pewne obawy związane z używaniem memset () w GCC 6.1 w funkcji samodzielnej. Kompilator GCC 6.1 na Godbolt może być zepsuty, ponieważ GCC 6.1 wydaje się generować normalną pętlę (jak 5.3 robi na Godbolt) dla samodzielnej funkcji dla niektórych ludzi. (Przeczytaj komentarze do odpowiedzi Zwol.)
volatile
jest błędem, chyba że udowodniono inaczej. Ale najprawdopodobniej błąd.volatile
jest tak niedookreślony, że jest niebezpieczny - po prostu go nie używaj.