Saya memerlukan fungsi yang (seperti SecureZeroMemory dari WinAPI) selalu memiliki memori nol dan tidak dapat dioptimalkan, bahkan jika kompiler berpikir bahwa memori tidak akan pernah diakses lagi setelah itu. Sepertinya kandidat yang sempurna untuk volatile. Namun saya mengalami beberapa masalah saat membuatnya berfungsi dengan GCC. Berikut ini contoh fungsinya:
void volatileZeroMemory(volatile void* ptr, unsigned long long size)
{
volatile unsigned char* bytePtr = (volatile unsigned char*)ptr;
while (size--)
{
*bytePtr++ = 0;
}
}
Cukup sederhana. Tetapi kode yang sebenarnya dihasilkan GCC jika Anda memanggilnya sangat bervariasi dengan versi kompilator dan jumlah byte yang sebenarnya Anda coba nol. https://godbolt.org/g/cMaQm2
- GCC 4.4.7 dan 4.5.3 tidak pernah mengabaikan volatile.
- GCC 4.6.4 dan 4.7.3 mengabaikan volatile untuk ukuran array 1, 2, dan 4.
- GCC 4.8.1 hingga 4.9.2 mengabaikan volatile untuk ukuran array 1 dan 2.
- GCC 5.1 hingga 5.3 mengabaikan volatile untuk ukuran array 1, 2, 4, 8.
- GCC 6.1 mengabaikannya untuk ukuran larik apa pun (poin bonus untuk konsistensi).
Kompiler lain yang telah saya uji (clang, icc, vc) menghasilkan penyimpanan yang diharapkan, dengan versi kompiler dan ukuran array apa pun. Jadi pada titik ini saya bertanya-tanya, apakah ini bug compiler GCC (cukup lama dan parah?), Atau apakah definisi volatile dalam standar tidak tepat bahwa ini sebenarnya merupakan perilaku yang sesuai, sehingga pada dasarnya tidak mungkin untuk menulis portabel "" Fungsi SecureZeroMemory?
Edit: Beberapa pengamatan menarik.
#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;
}
Kemungkinan penulisan dari callMeMaybe () akan membuat semua versi GCC kecuali 6.1 menghasilkan penyimpanan yang diharapkan. Mengomentari di pagar memori juga akan membuat GCC 6.1 menghasilkan penyimpanan, meskipun hanya dikombinasikan dengan kemungkinan penulisan dari callMeMaybe ().
Seseorang juga menyarankan untuk membersihkan cache. Microsoft tidak mencoba membersihkan cache sama sekali di "SecureZeroMemory". Cache kemungkinan besar akan dinonaktifkan dengan cukup cepat, jadi ini mungkin bukan masalah besar. Juga, jika program lain mencoba untuk menyelidiki data, atau jika itu akan ditulis ke file halaman, itu akan selalu menjadi versi nol.
Ada juga beberapa kekhawatiran tentang GCC 6.1 yang menggunakan memset () dalam fungsi standalone. Kompiler GCC 6.1 pada godbolt mungkin merupakan build yang rusak, karena GCC 6.1 tampaknya menghasilkan loop normal (seperti yang dilakukan 5.3 pada godbolt) untuk fungsi mandiri bagi sebagian orang. (Baca komentar jawaban zwol.)
volatile
adalah bug kecuali terbukti sebaliknya. Tapi kemungkinan besar bug.volatile
sangat tidak ditentukan sehingga berbahaya - jangan gunakan saja.