Bug #7040


gem install で特定の gem がインストールできない

Added by hsbt (Hiroshi SHIBATA) over 9 years ago. Updated over 9 years ago.

Target version:
ruby -v:
ruby 2.0.0dev (2012-09-20 trunk 36993) [x86_64-darwin12.2.0]


trunk で特定の gem(例えば libv8等)をインストールしようとすると必ず失敗してしまいます。

% gem i libv8
ERROR: While executing gem ... (Zlib::BufError)
buffer error

Twitter 上で nagachika さんから Zlib の GVL 対応?が原因ではないかとコメントを頂きましたが、上記のエラー表示以降は調査しきれていません。


zlib_inflate_buf_error.patch (634 Bytes) zlib_inflate_buf_error.patch nagachika (Tomoyuki Chikanaga), 10/08/2012 12:29 AM

Updated by nagachika (Tomoyuki Chikanaga) over 9 years ago

  • Assignee set to drbrain (Eric Hodel)


I'd like to switch to ruby-core, but I don't know how to do it on redmine...

Anyway, I've found that zstream_run_func() leaks Z_BUF_ERROR because inflate() could return Z_BUF_ERROR even when z->stream.avail_out > 0.
My tiny patch below prevent the exception, but I'm not confident at all it's right way to fix this issue.

diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index 6135e82..bcf289f 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -987,6 +987,7 @@ zstream_run_func(void *ptr)

    if (z->stream.avail_out > 0) {
        z->flags |= ZSTREAM_FLAG_IN_STREAM;
  •       err = Z_OK;

I think Eric (a.k.a drbrain) should have any idea, so I'll assign this ticket to him.


Updated by nagachika (Tomoyuki Chikanaga) over 9 years ago


I've investigated this little more deeper.

If inflate() (aka z->func->run()) return under condition which z->stream.avail_in == z->stream.avail_out == 0, current zstream_run_func() call inflate() once more even though there's no input available. In that case inflate() return Z_BUF_ERROR.

However, deflate() have hidden input buffer in z->stream.state (opaque structure) and should be called even when z->stream.avail_in == 0 (while z->stream.avail_out == 0).

I think zstream_run_func() should break from while loop when zstream->avail_in == 0 only if z->func->run == inflate.

I will commit an attached patch tomorrow if there's no objection.

ruby-dev なので日本語でも。


zstream_run_func() で inflate() (z->func->run()) がたまたま入出力のバッファがぴったり空/一杯になった (z->stream.avail_in == z->stream.avail_out == 0) で返ってきた時に、今の実装だとバッファを拡張して再度 inflate() を呼んでしまいます。zlib は入力が既にない状態で inflate() を呼ぶと Z_BUF_ERROR を返すようです。

ただ deflate() の場合は不可視な z->stream->state の構造体の中に隠れたバッファを持っているようで、z->stream.avail_in == 0 でももう一度 deflate() を呼ぶ必要がある場合があるようです。

添付したパッチのように z->func->run が inflate の時に限り z->stream.avail_in == 0 の時にも zstream_run_func() の while ループから抜けて、余分に inflate() (z->func->run())を呼ばないようにしてはどうかと思います。 make test-all は手元の環境では通りました。
実際に gem がインストールできない問題で困っているということなので、特に反対がなければ明日にでもコミットしておこうと思います。

Actions #3

Updated by nagachika (Tomoyuki Chikanaga) over 9 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r37119.
Hiroshi, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.

  • ext/zlib/zlib.c (zstream_run_func): don't call inflate() when
    z->stream.avail_in == 0. it return Z_BUF_ERROR.
    but deflate() could be called with z->stream->avail_in == 0 because
    it has hidden buffer in z->stream->state (opaque structure).
    fix for gem install error. [ruby-dev:46149] [Bug #7040]

Also available in: Atom PDF