logoalt Hacker News

jcranmerlast Tuesday at 2:57 PM2 repliesview on HN

Uninitialized memory should never be used as source of entropy. Most release software these days compiles using hardening flags, which will (at some levels) replace uninitialized memory with sentinel values, making the entropy of uninitialized memory frequently around 0.

But it gets worse. If the optimizer sees that you're loading uninitialized memory, it can reason that since the result of uninitialized memory is garbage, doing any computation on that result is also garbage, and happily delete said computation as a result. The cascading effect of this is to delete all of the entropy-mixing code, leaving your entropy pool with only the very low entropy source--giving uninitialized memory effectively negative entropy.

The net effect is that, at least for me, seeing someone trying to seed an entropy pool with uninitialized memory is a giant neon flashing sign saying "do not trust this code." It provides at best very little entropy and at worst actively destroys entropy and has other calamitous effects like valgrind or sanitizer errors, so you need to have other entropy sources anyways, so why bother?


Replies

mitxelayesterday at 11:47 AM

The code didn't rely on uninitialized memory as an entropy source. The patch was incorrect and cleared the buffer in two places: one with uninitialised memory, and one where it had been filled with seed entropy.

The code you're responsible for is the code that runs on the CPU. Compilers in the day could not optimize this away.

strenholmelast Tuesday at 4:38 PM

If the optimizer sees that you're loading uninitialized memory, it can reason that since the result of uninitialized memory is garbage, doing any computation on that result is also garbage, and happily delete said computation as a result

This is an interesting assertion, and one that is easy enough to prove true.

Let’s take the following C code, which uses the same XOF algorithm (but not implementation) as my application (Deadwood):

  #include<stdio.h>
  #include<stdint.h>
  #include<stdlib.h>
  #define b(z) for(c=0;c<z;c++)
  uint32_t c,e[42],f[42],g=19,h
  =13,n[45],i,j,k;void m(){j=0;
  b(12)f[c+c%3*h]^=e[c+1];b(g){
  i=c*7%g;k=e[i++];k^=e[i%g]|~e
  [(i+1)%g];j=j+c;n[c]=n[c+g]=k
  >>j%32|k<<-j%32;}for(i=39;i--
  ;f[i+1]=f[i])e[i]=n[i]^n[i+1]
  ^n[i+4];b(3)e[c+h]^=f[c*h]=f[
  c*h+h];*e^=1;}int main(int c,
  char**v){char*q=malloc(2);if(
  q==0)return 0;q[0]&=31;q[0]|=
  1;q[1]=0;for(;;m()){b(3){for(
  j=0;j<4;){f[c*h]^=k=(*q?255&
  *q:1)<<8*j++;e[c+16]^=k;if(!
  *q++){b(18)m();b(8){j=c;b(1)
  printf("%02x",(e[1+j%2]>>8*c)
  &255);c=j;if(c%2)m();}puts(
  "");return 0;}}}}}
This code, as I’m sure the parent poster can clearly see, uses four bits of uninitialized allocated memory as its source of entropy. As per the parent’s assertion, there should therefore exist a compiler whose optimizer will cause this XOF to not correctly run.

The above code can have one of the following possible 16 outputs:

  0a5d51f3745c7266
  f84b051f67115f1a
  f87105c4ecfefe67
  92074ac8e1e7a42e
  1441ac245f288e18
  87023372e57ae001
  047a3ddd14209546
  340b2ff47c61172e
  bfb9289ed096f977
  dfd56a7a8d7d723e
  2151460954a80242
  6822335c6e0160dc
  3783ce3cae3d0774
  4e0156df46c00bac
  69795d939d211e7a
If the above code has any but one of the above 16 outputs, this is a real world case where a C compiler, seeing uninitialized memory being used, optimizes out the code which uses said uninitialized memory as an input, and therefore will not output one of the above 16 possible words.

I’ve tested the above code in GCC -O3 and clang -O3; both generate one of the above 16 possible outputs (each one generating a different output).

If there really is a compiler out there which does “happily delete said computation”, which would give a different output than one of the 16 outputs above, please name that compiler, the version of said compiler used, and all compile-time flags used with said compiler.

While I’ve never heard of a real world case where a compiler would refuse to run code using uninitialized memory as yet another source of entropy for a secure PRNG, I do know of a real world case where a very nasty security hole was caused because someone incorrectly removed code using uninitialized memory as part of an entropy pool: CVE-2008-0166

show 1 reply