Tôi cần một chức năng (như SecureZeroMemory từ WinAPI) luôn không có bộ nhớ và không được tối ưu hóa, ngay cả khi trình biên dịch cho rằng bộ nhớ sẽ không bao giờ được truy cập lại sau đó. Có vẻ như là một ứng cử viên hoàn hảo cho sự biến động. Nhưng tôi đang gặp một số vấn đề khi thực sự làm việc này với GCC. Đây là một chức năng ví dụ:
void volatileZeroMemory(volatile void* ptr, unsigned long long size)
{
volatile unsigned char* bytePtr = (volatile unsigned char*)ptr;
while (size--)
{
*bytePtr++ = 0;
}
}
Đủ đơn giản. Nhưng mã mà GCC thực sự tạo ra nếu bạn gọi nó là rất khác nhau với phiên bản trình biên dịch và số byte mà bạn thực sự đang cố gắng bằng không. https://godbolt.org/g/cMaQm2
- GCC 4.4.7 và 4.5.3 không bao giờ bỏ qua những biến động.
- GCC 4.6.4 và 4.7.3 bỏ qua tính dễ bay hơi đối với kích thước mảng 1, 2 và 4.
- GCC 4.8.1 cho đến 4.9.2 bỏ qua biến động đối với kích thước mảng 1 và 2.
- GCC 5.1 cho đến 5.3 bỏ qua tính dễ bay hơi đối với kích thước mảng 1, 2, 4, 8.
- GCC 6.1 chỉ bỏ qua nó cho bất kỳ kích thước mảng nào (điểm thưởng cho tính nhất quán).
Bất kỳ trình biên dịch nào khác mà tôi đã thử nghiệm (clang, icc, vc) tạo ra các cửa hàng mà tôi mong đợi, với bất kỳ phiên bản trình biên dịch nào và bất kỳ kích thước mảng nào. Vì vậy, tại thời điểm này, tôi tự hỏi, liệu đây có phải là một lỗi biên dịch GCC (khá cũ và nghiêm trọng?) Hay là định nghĩa về biến động trong tiêu chuẩn không chính xác rằng đây thực sự là hành vi tuân thủ, khiến về cơ bản là không thể viết bản portable " Chức năng "SecureZeroMemory"?
Chỉnh sửa: Một số quan sát thú vị.
#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;
}
Việc ghi có thể có từ callMeMaybe () sẽ làm cho tất cả các phiên bản GCC ngoại trừ 6.1 tạo ra các cửa hàng dự kiến. Nhận xét trong hàng rào bộ nhớ cũng sẽ làm cho GCC 6.1 tạo ra các cửa hàng, mặc dù chỉ kết hợp với khả năng ghi từ callMeMaybe ().
Ai đó cũng đã đề xuất xóa bộ nhớ đệm. Microsoft không cố xóa bộ nhớ cache trong "SecureZeroMemory". Dù sao thì bộ nhớ đệm cũng có khả năng bị mất hiệu lực khá nhanh, vì vậy đây có lẽ không phải là vấn đề lớn. Ngoài ra, nếu một chương trình khác đang cố gắng thăm dò dữ liệu hoặc nếu nó sẽ được ghi vào tệp trang, nó sẽ luôn là phiên bản 0.
Cũng có một số lo ngại về GCC 6.1 sử dụng memset () trong hàm độc lập. Trình biên dịch GCC 6.1 trên godbolt có thể là một bản dựng bị hỏng, vì GCC 6.1 dường như tạo ra một vòng lặp bình thường (giống như 5.3 làm trên godbolt) cho chức năng độc lập đối với một số người. (Đọc bình luận về câu trả lời của zwol.)
volatile
là một lỗi trừ khi được chứng minh khác. Nhưng rất có thể là một lỗi.volatile
không được chỉ rõ là nguy hiểm - chỉ cần không sử dụng nó.