[Cryptography] Securely clearing memory (was: Heartbleed ...)

Patrick Chkoreff patrick at rayservers.net
Mon Apr 14 11:23:07 EDT 2014


Roland C. Dowdeswell wrote, On 04/11/2014 03:13 PM:

> The key point, though, is if you confuse the optimiser then it
> can't remove the writes.  I.e.:
> 
> 	size_t	i;
> 	size_t	accum;
> 	char	buf[SIZE];
> 
> 	for (i=0; i < sizeof(buf); i++)
> 		buf[i] = (i * 2) & 0xff;
> 
> 	for (i=0; i < sizeof(buf); i++)
> 		accum += buf[i];
> 
> 	if (accum % 2 == 1)
> 		/* Value can't be odd */
> 		abort();
> 
> Should confuse a majority of optimisers as I would assume that not
> many of them have logic that knows that the sum of a list of even
> numbers must also be even.  This is a little slower than simply
> zeroing the memory but it illustrates the point.


An optimizer might conceivably fold the two loops together like this:

  size_t i;
  size_t accum = 0;
  char buf[SIZE];

  for (i=0; i < sizeof(buf); i++)
      {
      char tmp = (i * 2) & 0xff;
      if (0) buf[i] = tmp; /* Don't bother storing tmp -- DOH! */
      accum += tmp;
      }

  if (accum % 2 = 1)
      /* Value can't be odd */
      abort();


I dunno, maybe if you cleared the buffer in one function, and then did
the sum/check in another, it might work -- but who knows?  The optimizer
might inline the functions and roll the loops together anyway.

I wish you could just call memset and be done with it, but apparently
optimizers know too much about the inner semantics of memset and can
optimize it away.

Gnupg uses "wipememory", which it implements as a macro but could
instead be a function.  Ignoring the vagaries of macro semantics, and
doing a little source code optimization of my own, wipememory is
essentially equivalent to this:

void wipememory(void *ptr, size_t len)
    {
    volatile char *vptr = (volatile char *)ptr;
    while (len--)
        *vptr++ = 0;
    }

That looks utterly equivalent to the Blake2 function that Bill Cox
mentioned earlier:

static inline void secureZeroMemory(void *v, uint32_t n)
    {
    volatile uint8_t *p = (volatile uint8_t *)v;
    while (n--)
        *p++ = 0;
    }

The common characteristic is casting to a volatile pointer.  Bill said
that trick might not work on all compilers, but if it's good enough for
both Gnupg and Blake2, I'd be hard-pressed to come up with anything better.


-- Patrick



More information about the cryptography mailing list