From a3e1444663005d3440b56c3a8e1b88bb28935912 Mon Sep 17 00:00:00 2001 From: "U.Nakamura" Date: Tue, 17 Oct 2023 20:30:28 +0900 Subject: [PATCH] merge revision(s) 96c5a4be7b0d72502001734770af0f4a735c544c: [Backport #19894] Fix memory leak in complemented method entries [Bug #19894] When a copy of a complemented method entry is created, there are two issues: 1. IMEMO_FL_USER3 is not copied, so the complemented status is not copied over. 2. In rb_method_entry_clone we increment both alias_count and complemented_count. However, when we free the method entry in rb_method_definition_release, we only decrement one of the two counters, resulting in the rb_method_definition_t being leaked. Co-authored-by: Adam Hess --- method.h | 5 +++-- test/ruby/test_module.rb | 29 +++++++++++++++++++++++++++++ vm_method.c | 8 +++++--- 3 files changed, 37 insertions(+), 5 deletions(-) --- method.h | 5 +++-- test/ruby/test_module.rb | 44 ++++++++++++++++++++++++++++++++++++++++ version.h | 4 ++-- vm_method.c | 10 +++++---- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/method.h b/method.h index 2f4504bfec9fda..93a23b0e269eb2 100644 --- a/method.h +++ b/method.h @@ -102,8 +102,9 @@ static inline void METHOD_ENTRY_FLAGS_COPY(rb_method_entry_t *dst, const rb_method_entry_t *src) { dst->flags = - (dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)) | - (src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)); + (dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2 + |IMEMO_FL_USER3)) | + (src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2|IMEMO_FL_USER3)); } typedef enum { diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index fa890c0e400cca..b5414d139ef94a 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -3176,6 +3176,50 @@ def test_module_name_in_singleton_method assert_match(/::Foo$/, mod.name, '[Bug #14895]') end + def test_iclass_memory_leak + # [Bug #19550] + assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true) + code = proc do + mod = Module.new + Class.new do + include mod + end + end + 1_000.times(&code) + PREP + 3_000_000.times(&code) + CODE + end + + def test_complemented_method_entry_memory_leak + # [Bug #19894] + assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true) + code = proc do + $c = Class.new do + def foo; end + end + + $m = Module.new do + refine $c do + def foo; end + end + end + + Class.new do + using $m + + def initialize + o = $c.new + o.method(:foo).unbind + end + end.new + end + 1_000.times(&code) + PREP + 100_000.times(&code) + CODE + end + private def assert_top_method_is_private(method) diff --git a/version.h b/version.h index e462d5b4880cf3..676f7f8f09152e 100644 --- a/version.h +++ b/version.h @@ -11,11 +11,11 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 4 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 242 +#define RUBY_PATCHLEVEL 243 #define RUBY_RELEASE_YEAR 2023 #define RUBY_RELEASE_MONTH 10 -#define RUBY_RELEASE_DAY 10 +#define RUBY_RELEASE_DAY 17 #include "ruby/version.h" diff --git a/vm_method.c b/vm_method.c index 7cebd2e3bce72f..f8f72f2cf2e3ab 100644 --- a/vm_method.c +++ b/vm_method.c @@ -651,11 +651,13 @@ rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, c const rb_method_entry_t * rb_method_entry_clone(const rb_method_entry_t *src_me) { - rb_method_entry_t *me = rb_method_entry_alloc(src_me->called_id, src_me->owner, src_me->defined_class, - method_definition_addref(src_me->def)); + rb_method_entry_t *me = rb_method_entry_alloc(src_me->called_id, src_me->owner, src_me->defined_class, src_me->def); if (METHOD_ENTRY_COMPLEMENTED(src_me)) { method_definition_addref_complement(src_me->def); } + else { + method_definition_addref(src_me->def); + } METHOD_ENTRY_FLAGS_COPY(me, src_me); return me; @@ -682,8 +684,8 @@ rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID cal def = NULL; } else { - def = method_definition_addref_complement(def); - } + method_definition_addref_complement(def); +} me = rb_method_entry_alloc(called_id, src_me->owner, defined_class, def); METHOD_ENTRY_FLAGS_COPY(me, src_me); METHOD_ENTRY_COMPLEMENTED_SET(me);