Derleyici belleğe bundan sonra bir daha erişilemeyeceğini düşünse bile (WinAPI'den SecureZeroMemory gibi) belleği her zaman sıfırlayan ve optimize edilmeyen bir işleve ihtiyacım var. Uçucu olmak için mükemmel bir aday gibi görünüyor. Ancak bunu GCC ile çalıştırmakta bazı sorunlar yaşıyorum. İşte örnek bir işlev:
void volatileZeroMemory(volatile void* ptr, unsigned long long size)
{
volatile unsigned char* bytePtr = (volatile unsigned char*)ptr;
while (size--)
{
*bytePtr++ = 0;
}
}
Yeterince basit. Ancak GCC'nin aslında onu çağırırsanız ürettiği kod, derleyici sürümüne ve aslında sıfırlamaya çalıştığınız bayt miktarına göre çılgınca değişir. https://godbolt.org/g/cMaQm2
- GCC 4.4.7 ve 4.5.3 uçucuları asla göz ardı etmez.
- GCC 4.6.4 ve 4.7.3, dizi boyutları 1, 2 ve 4 için uçucu yoksayar.
- 4.9.2'ye kadar GCC 4.8.1, 1 ve 2 dizileri için uçucu göz ardı eder.
- GCC 5.1, 5.3'e kadar 1, 2, 4, 8 dizi boyutları için uçucu yoksay.
- GCC 6.1, herhangi bir dizi boyutu için onu yok sayar (tutarlılık için bonus puan).
Test ettiğim herhangi bir başka derleyici (clang, icc, vc), herhangi bir derleyici sürümü ve herhangi bir dizi boyutu ile birinin beklediği depoları oluşturur. Bu noktada merak ediyorum, bu (oldukça eski ve ciddi bir GCC derleyici hatası mı?) Yoksa standarttaki geçici tanımı, bunun gerçekte uyumlu bir davranış olduğu konusunda kesin değil, bu da taşınabilir bir yazmayı esasen imkansız hale getiriyor " SecureZeroMemory "işlevi?
Düzenleme: Bazı ilginç gözlemler.
#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 () 'den olası yazma, 6.1 dışındaki tüm GCC sürümlerinin beklenen depoları oluşturmasını sağlayacaktır. Bellek çitine yorum yapmak, yalnızca callMeMaybe () 'den olası yazma ile kombinasyon halinde olsa da, GCC 6.1'in depoları oluşturmasını da sağlayacaktır.
Birisi ayrıca önbellekleri temizlemeyi önerdi. Microsoft, yok değil "SecureZeroMemory" in hiç önbelleğini temizlemek için deneyin. Önbellek muhtemelen oldukça hızlı bir şekilde geçersiz kılınacak, bu yüzden bu büyük bir sorun değil. Ayrıca, başka bir program verileri araştırmaya çalışıyorsa veya sayfa dosyasına yazılacaksa, bu her zaman sıfırlanmış sürüm olacaktır.
Bağımsız işlevde memset () kullanan GCC 6.1 ile ilgili bazı endişeler de vardır. GCC 6.1 bazı insanlar için bağımsız işlev için normal bir döngü oluşturuyor gibi göründüğü için (5.3 Godbolt'ta olduğu gibi), godbolt üzerindeki GCC 6.1 derleyicisi bozuk bir yapı olabilir. (Zwol'un cevabının yorumlarını okuyun.)
volatile
Aksi ispatlanmadıkça IMHO kullanımı bir hatadır. Ama büyük olasılıkla bir hata.volatile
tehlikeli olarak çok az tanımlanmıştır - sadece kullanmayın.