Ich benötige eine Funktion, die (wie SecureZeroMemory von WinAPI) den Speicher immer auf Null setzt und nicht optimiert wird, selbst wenn der Compiler glaubt, dass der Speicher danach nie wieder aufgerufen wird. Scheint ein perfekter Kandidat für volatile zu sein. Aber ich habe einige Probleme damit, dass dies mit GCC funktioniert. Hier ist eine Beispielfunktion:
void volatileZeroMemory(volatile void* ptr, unsigned long long size)
{
volatile unsigned char* bytePtr = (volatile unsigned char*)ptr;
while (size--)
{
*bytePtr++ = 0;
}
}
Einfach genug. Der Code, den GCC tatsächlich generiert, wenn Sie ihn aufrufen, hängt jedoch stark von der Compilerversion und der Anzahl der Bytes ab, die Sie tatsächlich auf Null setzen möchten. https://godbolt.org/g/cMaQm2
- GCC 4.4.7 und 4.5.3 ignorieren niemals die flüchtigen Bestandteile.
- GCC 4.6.4 und 4.7.3 ignorieren flüchtig für die Arraygrößen 1, 2 und 4.
- GCC 4.8.1 bis 4.9.2 ignorieren flüchtig für Arraygrößen 1 und 2.
- GCC 5.1 bis 5.3 ignorieren flüchtig für Arraygrößen 1, 2, 4, 8.
- GCC 6.1 ignoriert es einfach für jede Arraygröße (Bonuspunkte für Konsistenz).
Jeder andere von mir getestete Compiler (clang, icc, vc) generiert die erwarteten Speicher mit jeder Compilerversion und jeder Arraygröße. An diesem Punkt frage ich mich also, ob dies ein (ziemlich alter und schwerwiegender?) GCC-Compiler-Fehler ist oder ob die Definition von flüchtig im Standard ungenau ist, dass dies tatsächlich konform ist, was es im Wesentlichen unmöglich macht, ein tragbares Gerät zu schreiben. " SecureZeroMemory "Funktion?
Edit: Einige interessante Beobachtungen.
#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;
}
Durch das mögliche Schreiben von callMeMaybe () werden alle GCC-Versionen außer 6.1 die erwarteten Speicher generieren. Durch das Kommentieren im Speicherzaun generiert GCC 6.1 auch die Speicher, allerdings nur in Kombination mit dem möglichen Schreiben von callMeMaybe ().
Jemand hat auch vorgeschlagen, die Caches zu leeren. Microsoft versucht in "SecureZeroMemory" überhaupt nicht , den Cache zu leeren. Der Cache wird wahrscheinlich sowieso ziemlich schnell ungültig, daher ist dies wahrscheinlich keine große Sache. Wenn ein anderes Programm versucht hat, die Daten zu prüfen, oder wenn sie in die Auslagerungsdatei geschrieben werden sollen, ist dies immer die auf Null gesetzte Version.
Es gibt auch einige Bedenken bezüglich GCC 6.1, das memset () in der Standalone-Funktion verwendet. Der GCC 6.1-Compiler für Godbolt ist möglicherweise fehlerhaft, da GCC 6.1 für einige Benutzer eine normale Schleife (wie 5.3 für Godbolt) für die Standalone-Funktion zu generieren scheint. (Lesen Sie die Kommentare zu zwols Antwort.)
volatile
ist in diesem Fall angemessen.
memset
. Das Problem ist, dass Compiler genau wissen, was sie memset
tun.
volatile
Zeiger ergeben, auf den wir einen Zeiger wollen volatile
(es ist uns egal, ob er ++
streng ist, aber ob er *p = 0
streng ist).
volatile
ist ein Fehler, sofern nicht anders nachgewiesen. Aber höchstwahrscheinlich ein Fehler.volatile
ist so unterbestimmt, dass es gefährlich ist - benutze es einfach nicht.