C++ compilers are allowed to optimize away writes into memory:
{ //all this code can be eliminated char buffer[size]; std::fill_n( buffer, size, 0); }
When dealing with sensitive data the typical approach is using volatile*
pointers to ensure that memory writes are emitted by the compiler. Here's how SecureZeroMemory()
function in Visual C++ runtime library is implemented (WinNT.h):
FORCEINLINE PVOID RtlSecureZeroMemory( __in_bcount(cnt) PVOID ptr, __in SIZE_T cnt ) { volatile char *vptr = (volatile char *)ptr; #if defined(_M_AMD64) __stosb((PBYTE )((DWORD64)vptr), 0, cnt); #else while (cnt) { *vptr = 0; vptr++; cnt--; } #endif return ptr; }
The function casts the passed pointer to a volatile*
pointer and then writes through the latter. However if I use it on a local variable:
char buffer[size]; SecureZeroMemory( buffer, size );
the variable itself is not volatile
. So according to C++ Standard definition of observable behavior writes into buffer
don't count as observable behavior and looks like it can be optimized away.
Now there're a lot of comments below about page files, caches, etc, which are all valid, but let's just ignore them in this question. The only thing this question is about is whether the code for memory writes is optimized away or not.
Is it possible to ensure that code doing writes into memory is not optimized away in C++? Is the solution in SecureZeroMemory()
compliant to C++ Standard?
Compilers are free to optimize code so long as they can guarantee the semantics of the code are not changed. I would suggestion starting at the Compiler optimization wikipedia page as there are many different kinds of optimization that are performed at many different stages.
Code optimization is any method of code modification to improve code quality and efficiency. A program may be optimized so that it becomes a smaller size, consumes less memory, executes more rapidly, or performs fewer input/output operations.
There is no portable solution. If it wants to, the compiler could have made copies of the data while you were using it in multiple places in memory and any zero function could zero only the one it's using at that time. Any solution will be non-portable.
With library functions like SecureZeroMemory
, the library writers will typically have taken pains to ensure that such functions will not be inlined by the compiler. This means that in the snippet
char buffer[size]; SecureZeroMemory( buffer, size );
the compiler does not know what SecureZeroMemory
does with buffer
, so the optimizer can't prove that taking the snippet out does not affect the observable behaviour of the program. In other words, the library writers will already have done all that is possible to ensure such code is not optimized away.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With