2

Many crypto libraries include code similar to the following snippet:

/* Implementation that should never be optimized out by the compiler */
static void optimize_proof_zeroize( void *v, size_t n )
{
    volatile unsigned char *p = v;
    while( n-- ) *p++ = 0;
}

But my naive implementation doesn't survive an optimizing compiler:

/* Naive zeroization implementation */
static void naive_zeroize( unsigned char *c, size_t n)
{
    int i;
    for( i = 0; i < n; i++ )
       c[i] = 0;
}

The code is used to zeroize sensitive data before freeing the memory. Since the buffer is not used again, optimizing compilers assume that they can safely remove the zeriozation from the compiled code.

What prevents the first implemention from being optimized out?

  • 4
    You might want to read more about [the `volatile` qualifier](http://en.cppreference.com/w/c/language/volatile). – Some programmer dude May 22 '16 at 06:44
  • 1
    I see no reason any of the snippets would be optimized away. Perhaps you can [edit] your question to include a description of why you think the second snippet must be optimized away. – Artjom B. May 22 '16 at 10:49

1 Answers1

7

The key word here is volatile. When a variable is declared as volatile, it tells the compiler that this variable can be modified/accessed outside that program (by hardware for example) thus it forces the compiler not to optimize that variable and access the memory each time that variable is referenced.

The usage of it in crypto is usually to clear out the secret (keys) from the stack (local variables). Since the stack is used for the local variable, the regular code (like in your /* Naive zeroization implementation */) might not seem to have any impact on the other variables/state of the program, thus the compiler might (and probably will) optimize that code out. To prevent it, the volatile qualifier is used which makes the compiler leave that code and zero the memory content of the local variables.

Edit

Example:

void decrypt(void* src, void* dest, crypto_stuff_t* params)
{
    crypto_key_t decryption_key; // will hold the decryption key
    ....
    ....
    // end of flow
    // we want to zero the content of decryption_key, otherwise its value 
    // will remain on the stack
    // this:
    // decryption_key <-- 0;
    // will be just optimized out by the compiler
    // but this won't:
    volatile uint8_t* key_ptr = (uint8_t*)&decryption_key;
    int i;
    for(i = 0; i < sizeof(crypto_key_t); i++)
        key_ptr[i] = 0;
}
Alex Lop.
  • 6,810
  • 1
  • 26
  • 45