STLのrandomを使う場合の備忘

確認したのはC++ Builder 10.xのTokyoとRioだけなのでCLang一般ではないと思う。
しかし、インプリメントを確認しないで実装するとセキュリティホールになると思われる。

STLのrandomライブラリーはCベースのrandの代わりになる乱数ライブラリだ。
実用的な乱数発生器として重宝するのだが、シーダーのインプリメントによっては、乱数を推定できる。

mt19937の初期化として、よく紹介される以下のようなコードがある。

  mt19937 my_rand;
  random_device seeder;

  my_rand.seed( seeder() );

実際、適切にインプリメントされている場合、seeder()は良い乱数を発生させてmt19937の乱数発生器を初期化する。

残念ながら、C++ Builder 10.x系のCLang処理系において上記のテストを行うと、必ず同じ初期化子が渡されて初期化されることを確認した。
seederを繰り返し呼び出しても、常に同じ系列が返されるので予測可能となる。
確認のために次のコードを示す。

#include <iostream>
#include <random>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
  random_device seeder;

  cout << seeder();
  // 結果は常に同じ
  return 0;
}

実行するたびに常に同じ値が示されることが確認できるだろう。
mt19937についてもseedを設定しないで実行した場合、やはり同じ系列の乱数を返すので完全に予測が可能だ。
オブジェクトの生成毎に同じ乱数系列を設定するので、以下の例では同じ数字が3回繰り返される。

#include <iostream>
#include <random>
using namespace std;

void my_mt19937()
{
  mt19937 my;

  cout << my() << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
  my_mt19937();
  my_mt19937();
  my_mt19937();
	return 0;
}

乱数をつかっているつもりが、アプリケーションで乱数発生器の利用回数が分かれば乱数が簡単に推定されてしまう。

ということで、将来は分からないが現時点では乱数発生器へ渡すシードは昔ながらのアドホックに頼るのが一番ではないかと思う。
面倒なので、Windowsのみの場合を、他では時間をとるかな?

  mt19937 my;
  my.seed( ::GetTickCount() );

もっと良い方法があれば教えてほしい。

2020-09-30追記

C++ Builder 10.4および10.4.1でも状況は変わっていない。