Closed pascal-cuoq closed 8 years ago
Declaring the function in the header and having the implementation provided by a separate translation unit would be enough to make it work without LTO but that's not at all reassuring. I don't think volatile
is guaranteed to result in zeroing of the buffer itself here but it's better than nothing.
This approach is the best you can do for Clang and GCC but it's not portable so it would require an ifdef with an inferior alternative elsewhere:
void *explicit_memset(void *s, int c, size_t n) {
void *ptr = memset(s, c, n);
__asm__ __volatile__("" : : "r"(ptr) : "memory");
return ptr;
}
On Windows, you can use SecureZeroMemory
. If the compiler provides it, you can use memset_s
. On Mac OS X you can use memset_s
(IIUC).
The C11 standard introduced memset_s but it is the still the wrong idiom http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html and GCC can still translate that to nothing: http://goo.gl/LDfPHG (gcc.godbolt.org link by Samuel Neves).
The main problem with that code is that the provided memset_s
isn't compliant with the standard. I believe you can't write memset_s
in ISO C.
See https://github.com/briansmith/ring/issues/14 for some background info. Your explicit_memset
is exactly what BoringSSL's OPENSSL_cleanse
does except BoringSSL uses SecureZeroMemory
on Windows.
Thank You very much!
here is the fix: https://github.com/janmojzis/tinyssh/commit/7d9527f72d82dfe8bc3ec40a609dc87e3422d242
This report is for an instance of CWE-14 “Compiler Removal of Code to Clear Buffers” https://cwe.mitre.org/data/definitions/14.html
A
cleanup
macro is defined here and is used in several places to clear local variables of secrets: https://github.com/janmojzis/tinyssh/blob/97dd9e05f52482e46d660af81547c7c02669c1a2/crypto/cleanup.hFor instance on my computer, gcc invokes clang:
And the result of the compilation of the function
crypto_scalarmult_nistp256_tinynacl
, that invokes thecleanup
macro, is:The above is the translation of the source code below, from
crypto/crypto_scalarmult_nistp256.c
:The
for (i = 0; i < 64; ++i) q[i] = 0;
was translated to the series of 8movq
instructions. The code that follows is the canary check. The codecleanup(P); cleanup(Q);
was translated to nothing.The real GCC, or any modern optimizing C compiler, will do the same and translate the invocations of
cleanup()
on all local variables at the end of their scopes to nothing.There exists no perfect solution for this problem. The C11 standard introduced
memset_s
but it is the still the wrong idiom http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html and GCC can still translate that to nothing: http://goo.gl/LDfPHG (gcc.godbolt.org link by Samuel Neves). Also not everyone is using a C11 compiler yet.I found that if I simply convert the array passed as argument to the
cleanup()
macro to avolatile
pointer, my compiler does generate the code to clean up the local arrays:Using the
volatile
qualifier this way is not perfect either, but it at least tricks some current compilers into doing the right thing, which is somewhat better than the reliable elimination of dead stores that happens without it.