From: shyouhei@... Date: 2017-09-10T12:24:25+00:00 Subject: [ruby-dev:50234] [Ruby trunk Bug#13885] Random.urandom と securerandom について Issue #13885 has been updated by shyouhei (Shyouhei Urabe). kosaki (Motohiro KOSAKI) wrote: > >1.Random.urandom は、getrandom(2) や (/dev/urandom に対する) read(2) システムコールを 1 回しか呼ばないようです。よって、要求量が大きくて 1 回では文字列を準備できないとき(例えば > > Random.urandom(100_000_000) )、失敗して nil を返します。これは意図的でしょうか。要求量が得られるまで(または他の要因で失敗するまで)繰り返し呼ぶべきではないでしょうか。 > > そんな気がします。一瞬エントロピー枯渇について心配しましたが、Rubyの側で小さいurandomを大量に呼べばどうせ枯渇するんだし 昔のLinuxのmanに "Users should be very economical in the amount of seed material that they read from /dev/urandom" とか書いてあったのがループしない経緯ではないかと想像しています。その記述はなくなったので、今となってはループしない理由はないんではないかなと思いますがいかがでしょうか。 (まあループの中でエントロピー吸い出すなら当然ブロッキングIOということになるわけで、GVL放した方がいいんじゃないのとか思わなくもないのでパッチはやや大きそうではあると感じますが) > >2.Random.urandom は失敗するとき、nil ではなく例外を投げるほうがよいのではないでしょうか。nil を返して実行が進んで幸せになるケースはあまり思い浮かびませんでした。 > > 同意します > どうせ実アプリケーションでurandom直接呼んでる人なんていないのだから気にせず変えてしまっていいと思います Random.urandomという名前になったのは2.5からなので、今ならまだ実アプリケーションがどうとか悩む必要もなく、いきなり変えてOKのはずです。 > >4.securerandom は、初回の呼び出しで Random.urandom が成功したらその後ずっと Random.urandom を使い、失敗したらずっと openssl を使うようになっています。 > >これは本当に初回の呼び出しだけで振り分けるのでよいのでしょうか。具体的には、Random.urandom(0) は常に成功する(空文字列を返す)ので、実際には urandom が > >使えない環境でも urandom に固定される可能性があります。 > >逆に、securerandom のバックエンドを動的に切り替えるようにしたら問題あるでしょうか。最初は成功していた Random.urandom が途中から失敗するようになったとき、 > >openssl へフォールバックしても良いものでしょうか。また、Random.urandom が復活した場合、openssl から Random.urandom に戻すのは問題ないでしょうか。 > > 途中でurandomが使えなくなったり使えるようになったりってのは、1の長大な引数のケースぐらいしか思いつかないので、 > 固定でいいのではないでしょうか。 0の件は後述。Random.urandomがきちんとループして要求されたバイト数まで読むようになった場合、Random.urandomの実行結果は以下の2パターンのいずれかになるはずです。 - 成功して、必要なだけのデータが入手できる - 失敗して、例外が上がる さて、この整理が済んだ後、urandomが「折に触れて成功したり失敗したりする」というのは考えられるでしょうか。なんか失敗する時はずっと失敗するのではないかという気がします。なので、バックエンドの切り替えが固定かどうかはさほど重要な論点ではなくなる気がしています。 > それはそれとしてRandom.urandom(0) の件はバグじゃないのかと思います。 これに関してはまず、urandom(0)で何が返るべきかですが、空文字列だと思います。 同様の例としてread(0)で何が返るべきかという問題が「APIデザインケーススタディ」の21ページ付近に載っているので読むといいと思うんですが、要は可変長の文字列が欲しいという場合がありえて、意味のある引数として0が渡ってくる可能性がある。そこで戻り値にnilを返しうることにしてしまうと、呼び出し側でnilかどうか毎回チェックせねばならず面倒なわけです。なのでこれは食う文字列であるべき。 一方で、そうすると、初回の引数が0だったときにSecureRandomが意図しない動きになってしまうじゃないかという話はあって、これは回避する必要がありそうです。思いつくのは、 - たとえ引数が0であってもシステムコールやデバイスファイル読み出しを律儀に行い、きちんと例外をあげていく - urandomとは別に現在利用可能な乱数元の一覧を得るAPIを新設する くらいですが、後者はやや話が大きいので、前者かなあと思います。 ---------------------------------------- Bug #13885: Random.urandom と securerandom について https://bugs.ruby-lang.org/issues/13885#change-66581 * Author: mame (Yusuke Endoh) * Status: Open * Priority: Normal * Assignee: * Target version: * ruby -v: ruby 2.5.0dev (2017-09-09 trunk 59792) [x86_64-linux] * Backport: 2.2: UNKNOWN, 2.3: UNKNOWN, 2.4: UNKNOWN ---------------------------------------- 遠藤です。 Random.urandom と securerandom の仕様について卜部さんと話していて、いくつか気になる点が出てきたので挙げておきます。 1. Random.urandom は、getrandom(2) や (/dev/urandom に対する) read(2) システムコールを 1 回しか呼ばないようです。よって、要求量が大きくて 1 回では文字列を準備できないとき(例えば Random.urandom(100_000_000) )、失敗して nil を返します。これは意図的でしょうか。要求量が得られるまで(または他の要因で失敗するまで)繰り返し呼ぶべきではないでしょうか。 2. Random.urandom は失敗するとき、nil ではなく例外を投げるほうがよいのではないでしょうか。nil を返して実行が進んで幸せになるケースはあまり思い浮かびませんでした。 3. Random.urandom が nil を返す可能性があることは rdoc で明記されるべきではないでしょうか。(とりあえず r59803 で書き足しました。まずかったらすみません) 4. securerandom は、初回の呼び出しで Random.urandom が成功したらその後ずっと Random.urandom を使い、失敗したらずっと openssl を使うようになっています。これは本当に初回の呼び出しだけで振り分けるのでよいのでしょうか。具体的には、Random.urandom(0) は常に成功する(空文字列を返す)ので、実際には urandom が使えない環境でも urandom に固定される可能性があります。 5. 逆に、securerandom のバックエンドを動的に切り替えるようにしたら問題あるでしょうか。最初は成功していた Random.urandom が途中から失敗するようになったとき、openssl へフォールバックしても良いものでしょうか。また、Random.urandom が復活した場合、openssl から Random.urandom に戻すのは問題ないでしょうか。 -- https://bugs.ruby-lang.org/