Optimize for security, not just for speed
Written by Jiří Keresteš on 2019-05-09
securityIn today’s article, we’re going to explain why you should think twice before enabling compiler optimizations.
If you are a developer, you are probably used to pass something like
-O2
to your compiler of choice. Optimized code is supposed to run faster,
why wouldn’t you want that? Right? Well, your code will probably be faster, but some
optimizations can actually hurt security. Let’s see an example:
uint8_t buffer[512];
void clear(uint8_t *target, uint32_t size){
memset(target, 0, size);
}
int main() {
// sensitive data processing
memset(buffer, 'A', sizeof(buffer));
for(int i=0; i < sizeof(buffer); i++){
putchar(buffer[i]);
}
// clear buffer
clear(buffer, sizeof(buffer));
return 0;
}
Our sample program does some data processing and then cleans up. Or does it? Let’s look at the assembly generated by gcc compiler with no optimizations (thanks to Compiler Explorer).
As you can see, main function does all the things we expect from it. That’s good, in
practice it means no leftover secrets in memory. What happens with
-O2
?
Output assembly is a little less clear, but we can see that call to function
clear()
got optimized out. From performance viewpoint, this is a perfectly
valid optimization called dead store elimination or DSE. We are modifying
buffer content without using it afterwards. Leaving it out saves some CPU cycles, which
improves speed, but cripples security. This doesn’t mean you should compile all your
code without optimizations from now on. There are multiple techniques that prevent
compiler from optimizing away your memory scrubbing. You can use platform specific
functions, such as explicit_bzero
, sodium_memzero
or
SecureZeroMemory
. If you don’t have these, memory barriers or volatile
buffers are usually way to go. Of course, this is not the only optimization pitfall you
can encounter. If you want to know more about this topic, I personally recommend
great 35C3 talk by Ilja van
Sprundel. To be extra sure, you can always check what your binary does with
excellent radare2 framework, but that’s for
another post. That would be all for today, see you next Thursday!