We're concerned a malicious program will attempt to read a password out of RAM. So we wrote a function to overwrite a password variable, then free it. Our concern is that a smart compiler like clang or gcc will optimize out the code inside this function. The function changes every character in the string to a null byte and then frees the variable.
void free_pword(char* pword) {
char* pword_original = pword;
char c;
while ((c = *(pword++))) {
*pword = '\0';
}
free(pword_original);
}
Now our questions. Do we need to do this? If not, why? If we do need to do it, how can we be assured that the overwrite will not be optimized away?
########### EDIT 1: (February 11 2015)DEAR PEOPLE FROM THE FUTURE: Here's what we've figured out so far ...
I looked at the raw assembly code (from gcc) to determine wether or not the code was optimized away, it was not. I decided to add volatile in case the optimizer changes in an update.
Version of gcc tested.
$ gcc -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.1.0
Thread model: posix
I did not make and accept my own answer because I'm not 100% sure this is secure for all compilers. A expert on compilers may be able to answer with certainty.
If you need to remove a password securely like we do before freeing please test this function using your compiler and look at the raw assembly. You don't have to understand the assembly just make sure the ___bzero function (memset) and the _free function is called.
Current working version:
void free_pword(char * pword) {
//instead of changing the argument to the function, I added a volatile variable
//this was I don't have to change the calls to this function to remove warnings
char volatile * volatile_pword = pword;
memset((char *)volatile_pword, 0, strlen((char *)volatile_pword));
free((char *)volatile_pword);
}
It is entirely possible that a compiler optimizes a call to memset
away. That's why there are functions like memset_s
(C11) and explicit_bzero
(BSD) to securely erase a block of memory.
I also think that using a volatile function pointer is more reliable than casting to a pointer to volatile:
void *(*volatile forced_memset)(void *, int, size_t) = memset;
More tricks like inserting an asm
statement or a memory barrier can be found in this great thread on the musl mailing list. There you'll also find a pointer to this interesting post on how to portably test whether any of these approaches work.
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