Description
While investigating a performance drop (10<->20% post meltdown) we noticed this line of code:
if (dcfg->crypto_key == NOT_SET_P) dcfg->crypto_key = getkey(dcfg->mp);
Which calls getkey(), which then does this:
apr_generate_random_bytes(salt, sizeof(salt));
key = apr_psprintf(mp,"%.s",(int)sizeof(salt),salt);
apr_sha1_init (&ctx);
apr_sha1_update (&ctx, (const char)key, strlen(key));
apr_sha1_update (&ctx, "\0", 1);
apr_generate_random_bytes(salt, sizeof(salt));
value = apr_psprintf(mp,"%.*s",(int)sizeof(salt),salt);
It's calling apr_generate_random_bytes() 2 times, which with APR 1.5 (an all other versions of APR) reads /dev/urandom - This seems to be read 2x for each connection to Apache.
Post meltdown, we are seeing this with a simple open()/read() of urandom:
nothing:: 44 cycles
/dev/urandom:: 38244 cycles
getrandom syscall:: 3637 cycles
rdrand raw asm:: 44 cycles
rdrand raw asm with loops:: 51 cycles
rdrand64 intrinsics::: 44 cycles
Here's some performance metrics with a server on the same LAN, over https
Requests per second: 2301.54 [#/sec] (mean)
Setting the following in modsec2.conf, ie:
SecHashKey "foobar" KeyOnly
Give the following:
Requests per second: 2671.05 [#/sec] (mean)
The bug is that mod security should use a static, session (running) key if SecHashKey == NULL from the conf, and drop the per connection getkey() to avoid a performance loss.
Another option is to use cpuid() to see if the cpu supports rdrand/rdseed, then use that as a random number generator, or better yet, use getrandom() syscall from Linux 3.17+ which is about 5 to 10x faster until APR decides to use getrandom()