From: mame@... Date: 2017-09-10T14:52:13+00:00 Subject: [ruby-dev:50235] [Ruby trunk Bug#13885] Random.urandom と securerandom について Issue #13885 has been updated by mame (Yusuke Endoh). shyouhei (Shyouhei Urabe) wrote: > (まあループの中でエントロピー吸い出すなら当然ブロッキングIOということになるわけで、GVL放した方がいいんじゃないのとか思わなくもないのでパッチはやや大きそうではあると感じますが) /dev/urandom はブロックしない(少なくとも最近の OS なら)、という前提があっても、GVL 放すべきですかね? > なんか失敗する時はずっと失敗するのではないかという気がします。 小崎さんと卜部さんの 2 人がそういうなら、大丈夫な気がしてきました。 > - たとえ引数が0であってもシステムコールやデバイスファイル読み出しを律儀に行い、きちんと例外をあげていく これはちょっと安心できない気がします。0 バイトの読み出しに対して何もせずに成功を返すというのは Ruby に限らずありがちな実装だと思うので、getrandom(2) システムコールが同じことをしても驚かないです(仮に今の実装がそうなっていなくても、man などで明記されていない限り、将来的に変わるかもしれない)。 Random.urandom の挙動は今のまま変えず、securerandom は初回の読み出しで(要求が 0 バイトであろうとも)必ず 1 バイト以上読み出してみる、というのが手軽な対策だと思ってます。異論なければ以下のパッチをコミットしようと思いますが、どうでしょうか。 ~~~~ diff --git a/lib/securerandom.rb b/lib/securerandom.rb index 6a5720c44e..e20591a64f 100644 --- a/lib/securerandom.rb +++ b/lib/securerandom.rb @@ -52,7 +52,7 @@ def bytes(n) end def gen_random(n) - ret = Random.urandom(n) + ret = Random.urandom(1) if ret.nil? begin require 'openssl' @@ -67,10 +67,6 @@ class << self end return gen_random(n) end - elsif ret.length != n - raise NotImplementedError, \ - "Unexpected partial read from random device: " \ - "only #{ret.length} for #{n} bytes" else @rng_chooser.synchronize do class << self ~~~~ ---------------------------------------- Bug #13885: Random.urandom と securerandom について https://bugs.ruby-lang.org/issues/13885#change-66586 * 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/