Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
Already on GitHub? Sign in to your account
[wallet] Securely erase potentially sensitive keys/values #10308
Conversation
fanquake
added
the
Wallet
label
May 1, 2017
| @@ -8,6 +8,8 @@ | ||
| #include <stdlib.h> | ||
| +// Securely erase a span of memory in a way guaranteed not to be |
tjps
May 2, 2017
Contributor
While it's true that there is nothing in the standard that guarantees this, there are numerous compiler-specific attributes for this, as well as libraries that guarantee this (like right now, memory_cleanse is just a wrapper around OPENSSL_cleanse)
My goal with adding the comment is to explain its purpose to other maintainers of the codebase, as it hopefully sees more use.
tjps
May 2, 2017
Contributor
On further research, I'm not sure OPENSSL_cleanse actually guarantees anything at all.
gmaxwell
May 3, 2017
Member
On further research, I'm not sure OPENSSL_cleanse actually guarantees anything at all.
Yep.
tjps
May 3, 2017
Contributor
To be fair in this case since OPENSSL_cleanse() exists in an external shared object, we know it won't be optimized out. Should I back out this comment though?
sipa
May 3, 2017
•
Owner
@tjps Nothing in C++ can guarantee that memory will be overwritten. The memset_s function from the C11 standard does require a guarantee, but gcc/g++ don't implement it I believe. So, writing in the source code that something has a guarantee may be misleading. Furthermore, the memory being overwritten is not the only thing we're after. The compiler is free to for example copy some of the memory to the stack first, where it could live longer.
However, if we restrict ourselves to supported systems and platforms, we know more at this point in time. We know what optimizations are implemented, and what compilers are capable of. It is reasonable that a call won't be optimized out if it's in another module, but that may not be the case if we'd switch to LTO builds, for example.
I would just write "Attempt to overwrite a span of memory" or something.
tjps
May 3, 2017
Contributor
Agreed, I reduced the verbiage to make it clear that it is a best-effort attempt.
Also, it is my understanding that LTO only affects the object files you are linking, so dynamically loaded .so's are still safe from compiler analysis. But I could be wrong, I've never actually come across a project of any significant size using LTO.
Comment updated.
| + memory_cleanse(datKey.get_data(), datKey.get_size()); | ||
| + bool success = false; | ||
| + if (datValue.get_data() != NULL) | ||
| + { |
tjps
May 2, 2017
Contributor
That's actually my preference as well, but I thought that was against the style guide for this repo. Can I assume brace on same line for code going forward?
| + CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); | ||
| + ssValue >> value; | ||
| + success = true; | ||
| + } catch (const std::exception&) { |
laanwj
May 2, 2017
Owner
Silently ignored exceptions are one of the larger frustrations when troubleshooting C++ code. Checking more closely it seems this is ok: please add a comment e.g. /* success = false */ here to show what is happening.
|
LGTM, utACK, as we have the |
|
utACK 2ac5445 |
tjps commentedMay 1, 2017
Doing an audit of "memset(.*0" usage I found a few locations that intended to clear potentially sensitive memory right before freeing it, but were not using the nice memory_cleanse() function in the codebase. I have not verified that the compiler was actually eliding those calls, but better to be safe than sorry. Also changed one path that potentially left a value unscrubbed if it threw while deserializing.