Bug #7040


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

Added by hsbt (Hiroshi SHIBATA) over 11 years ago. Updated over 11 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 11 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 11 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 11 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