[前][次][番号順一覧][スレッド一覧]

ruby-changes:71671

From: NAKAMURA <ko1@a...>
Date: Thu, 7 Apr 2022 22:52:52 +0900 (JST)
Subject: [ruby-changes:71671] f740ffb81f (ruby_2_7): merge revision(s) b3d62a77d928eff01268ca7fa1c1c0966702926d [Backport #17803]

https://git.ruby-lang.org/ruby.git/commit/?id=f740ffb81f

From f740ffb81f3ef11526add528583b0cbfce28af67 Mon Sep 17 00:00:00 2001
From: NAKAMURA Usaku <usa@r...>
Date: Sat, 5 Mar 2022 21:38:48 +0900
Subject: merge revision(s) b3d62a77d928eff01268ca7fa1c1c0966702926d [Backport
 #17803]

    [ruby/zlib] Synchronize access to zstream to prevent segfault in
     multithreaded use

    I'm not sure whether this handles all multithreaded use cases,
    but this handles the example that crashes almost immediately
    and does 10,000,000 total deflates using 100 separate threads.

    To prevent the tests from taking forever, the committed test
    for this uses only 10,000 deflates across 10 separate threads,
    which still causes a segfault in the previous implementation
    almost immediately.

    Fixes [Bug #17803]

    https://github.com/ruby/zlib/commit/4b1023b3f2
    ---
     ext/zlib/zlib.c        | 33 ++++++++++++++++++++++++++-
     test/zlib/test_zlib.rb | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++
     2 files changed, 93 insertions(+), 1 deletion(-)
---
 ext/zlib/zlib.c        | 32 ++++++++++++++++++++++++++++-
 test/zlib/test_zlib.rb | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++
 version.h              |  8 ++++----
 3 files changed, 91 insertions(+), 5 deletions(-)

diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index 3b8e097da9..bfd55ffe34 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -530,6 +530,7 @@ struct zstream { https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L530
     unsigned long flags;
     VALUE buf;
     VALUE input;
+    VALUE mutex;
     z_stream stream;
     const struct zstream_funcs {
 	int (*reset)(z_streamp);
@@ -602,6 +603,7 @@ zstream_init(struct zstream *z, const struct zstream_funcs *func) https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L603
     z->flags = 0;
     z->buf = Qnil;
     z->input = Qnil;
+    z->mutex = rb_mutex_new();
     z->stream.zalloc = zlib_mem_alloc;
     z->stream.zfree = zlib_mem_free;
     z->stream.opaque = Z_NULL;
@@ -631,7 +633,9 @@ zstream_expand_buffer(struct zstream *z) https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L633
 
 	    rb_obj_reveal(z->buf, rb_cString);
 
+	    rb_mutex_unlock(z->mutex);
 	    rb_protect(rb_yield, z->buf, &state);
+	    rb_mutex_lock(z->mutex);
 
 	    z->buf = Qnil;
 	    zstream_expand_buffer_into(z, ZSTREAM_AVAIL_OUT_STEP_MAX);
@@ -1025,7 +1029,7 @@ zstream_unblock_func(void *ptr) https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L1029
 }
 
 static void
-zstream_run(struct zstream *z, Bytef *src, long len, int flush)
+zstream_run0(struct zstream *z, Bytef *src, long len, int flush)
 {
     struct zstream_run_args args;
     int err;
@@ -1109,6 +1113,31 @@ loop: https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L1113
 	rb_jump_tag(args.jump_state);
 }
 
+struct zstream_run_synchronized_args {
+    struct zstream *z;
+    Bytef *src;
+    long len;
+    int flush;
+};
+
+static VALUE
+zstream_run_synchronized(VALUE value_arg)
+{
+    struct zstream_run_synchronized_args *run_args = (struct zstream_run_synchronized_args *)value_arg;
+    zstream_run0(run_args->z, run_args->src, run_args->len, run_args->flush);
+    return Qnil;
+}
+
+static void
+zstream_run(struct zstream *z, Bytef *src, long len, int flush)
+{
+    struct zstream_run_synchronized_args run_args;
+    run_args.z = z;
+    run_args.src = src;
+    run_args.len = len;
+    run_args.flush = flush;
+    rb_mutex_synchronize(z->mutex, zstream_run_synchronized, (VALUE)&run_args);
+}
 static VALUE
 zstream_sync(struct zstream *z, Bytef *src, long len)
 {
@@ -1154,6 +1183,7 @@ zstream_mark(void *p) https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L1183
     struct zstream *z = p;
     rb_gc_mark(z->buf);
     rb_gc_mark(z->input);
+    rb_gc_mark(z->mutex);
 }
 
 static void
diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb
index 7d703d15e4..e4baa44ecb 100644
--- a/test/zlib/test_zlib.rb
+++ b/test/zlib/test_zlib.rb
@@ -3,6 +3,7 @@ https://github.com/ruby/ruby/blob/trunk/test/zlib/test_zlib.rb#L3
 require 'test/unit'
 require 'stringio'
 require 'tempfile'
+require 'securerandom'
 
 begin
   require 'zlib'
@@ -443,6 +444,61 @@ if defined? Zlib https://github.com/ruby/ruby/blob/trunk/test/zlib/test_zlib.rb#L444
       assert_raise(Zlib::StreamError) { z.set_dictionary("foo") }
       z.close
     end
+
+    def test_multithread_deflate
+      zd = Zlib::Deflate.new
+      s = "x" * 10000
+      (0...10).map do |x|
+        Thread.new do
+          1000.times { zd.deflate(s) }
+        end
+      end.each do |th|
+        th.join
+      end
+    ensure
+      zd&.finish
+      zd&.close
+    end
+
+    def test_multithread_inflate
+      zi = Zlib::Inflate.new
+      s = Zlib.deflate("x" * 10000)
+      (0...10).map do |x|
+        Thread.new do
+          1000.times { zi.inflate(s) }
+        end
+      end.each do |th|
+        th.join
+      end
+    ensure
+      zi&.finish
+      zi&.close
+    end
+
+    def test_recursive_deflate
+      zd = Zlib::Deflate.new
+      s = SecureRandom.random_bytes(1024**2)
+      assert_raise(Zlib::BufError) do
+        zd.deflate(s) do
+          zd.deflate(s)
+        end
+      end
+    ensure
+      zd&.finish
+      zd&.close
+    end
+
+    def test_recursive_inflate
+      zi = Zlib::Inflate.new
+      s = Zlib.deflate(SecureRandom.random_bytes(1024**2))
+      assert_raise(Zlib::DataError) do
+        zi.inflate(s) do
+          zi.inflate(s)
+        end
+      end
+    ensure
+      zi&.close
+    end
   end
 
   class TestZlibGzipFile < Test::Unit::TestCase
diff --git a/version.h b/version.h
index 960bec511d..750dc066e5 100644
--- a/version.h
+++ b/version.h
@@ -2,11 +2,11 @@ https://github.com/ruby/ruby/blob/trunk/version.h#L2
 # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
 #define RUBY_VERSION_TEENY 6
 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
-#define RUBY_PATCHLEVEL 209
+#define RUBY_PATCHLEVEL 210
 
-#define RUBY_RELEASE_YEAR 2021
-#define RUBY_RELEASE_MONTH 12
-#define RUBY_RELEASE_DAY 31
+#define RUBY_RELEASE_YEAR 2022
+#define RUBY_RELEASE_MONTH 3
+#define RUBY_RELEASE_DAY 5
 
 #include "ruby/version.h"
 
-- 
cgit v1.2.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]