[cryptography] Just how bad is OpenSSL ?
solar at openwall.com
Mon Oct 29 19:31:03 EDT 2012
On Mon, Oct 29, 2012 at 04:06:58PM -0400, Jeffrey Walton wrote:
> On Sun, Oct 28, 2012 at 3:01 PM, Solar Designer <solar at openwall.com> wrote:
> > The OPENSSL_cleanse() function is such that the memory is overwritten
> > with the counter values, whereas the counter is incremented in ways
> > dependent on the pointer to the memory region, and it is used to
> > "pre-initialize" regions being allocated by OpenSSL's CRYPTO_malloc().
> > This creates a non-trivial inter-dependency between the memory allocator
> > and the memory sanitization functions, which presumably a compiler won't
> > recognize as something that can be optimized out with no effect on
> > operation of correctly written programs.
> > Frankly, I do find this specific approach questionable, but the
> > rationale is there, and the compiler optimization problem that this
> > weird approach is trying to solve is real.
> Out of curiosity, how would you do it?
The approach I am using lately is to run a quick self-test right after
performing the actual crypto task using the same code. I also make it
likely (to the extent possible from C source) that both calls are made
from the same scope (same initial stack pointer value). As long as the
crypto and the self-test are together complicated enough that the
compiler does not do any of it at compile time, this works.
However, this approach would not be usable by many of OpenSSL's
interfaces, which may be too performance-critical, or in some cases they
may be too trivial (e.g., if some function merely copies data then its
self-test would be fairly likely to be optimized out).
> Perhaps cleanse() should go in
> a separate source file compiled with no optimizations (-O0)?
It is already in a separate source file. I think it is compiled with
the same optimization as the rest of OpenSSL now.
The counter updates and the dependency on the memory allocator in the
current OPENSSL_cleanse() make it likely that as long as there's just
one instance of OPENSSL_cleanse() code in the entire program then
neither the writes inside this function nor calls to it are optimized
out. So this inter-dependency does play a useful role. (The writes
can't be optimized out because there's just one instance of the code,
and hopefully at least one of its uses potentially needs the writes as
far as the compiler is aware. The calls can't be optimized out because
they not only make the writes, but also update the counter, which is
sort of made use of via the memory allocator.)
However, with possible link time optimization there could be multiple
instances of OPENSSL_cleanse() (e.g., some of them could be inlined),
and the writes could then be optimized out of some of the instances.
> GCC uses volatile strictly for memory mapped hardware
> (http://gcc.gnu.org/ml/gcc-help/2012-03/msg00251.html), so its an
> abuse that the value is changed by software.
This thread on gcc-help talks about two possible uses of volatile: for
memory-mapped I/O (correct) and for communication between threads (wrong
unless memory barriers are also used). However, these are not the only
possible uses. volatile is also useful for communication within one
process (one thread), typically when the main program wants to check
whether one of its signal handlers has been called. The signal handler
updates a variable and returns. The main program reads the volatile
variable from inside a loop and becomes aware of the event. Without the
volatile keyword, it would potentially be reading a cached copy of the
variable (usually a register), so it would not learn of the event until
much later (likely until exiting/re-entering the function), if at all.
> The OpenSSL cleanse() function will likely fail on BIOs created from
> storage and memory mapped files when used on SSDs due to write
> leveling and on-controller compression. If write leveling goes away,
> it looks like cleanse() will still fail due to compression. Hence the
> need for random, non-compressible data.
Not overwriting the same location may also happen due to journaling
I think OPENSSL_cleanse() is generally meant to be called shortly after
the data had been written to this memory location (and made use of),
before the data would likely hit external media. If we're lucky (as
expected), only the overwritten data (such as the trivial pattern
produced by OPENSSL_cleanse() currently) would potentially hit external
media, and then it almost does not matter whether it's all zeroes or
some pattern. (Well, it may matter for reasons other than
confidentiality of the original data in that memory location - e.g.,
leaking the fact that a certain OpenSSL interface has been used at least
a certain number of times.)
More information about the cryptography