ฉันต้องการฟังก์ชันที่ (เช่น 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 จะละเว้นมันสำหรับขนาดอาร์เรย์ใด ๆ (คะแนนโบนัสสำหรับความสม่ำเสมอ)
คอมไพเลอร์อื่น ๆ ที่ฉันได้ทดสอบ (เสียงดัง, 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
ถูกระบุว่าเป็นอันตรายมาก - อย่าใช้มัน