Skip to content

Commit

Permalink
merge revision(s) 96c5a4b: [Backport #19894]
Browse files Browse the repository at this point in the history
    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 <[email protected]>
    ---
     method.h                 |  5 +++--
     test/ruby/test_module.rb | 29 +++++++++++++++++++++++++++++
     vm_method.c              |  8 +++++---
     3 files changed, 37 insertions(+), 5 deletions(-)
  • Loading branch information
unak committed Oct 17, 2023
1 parent 84f2aab commit a3e1444
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 8 deletions.
5 changes: 3 additions & 2 deletions method.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
44 changes: 44 additions & 0 deletions test/ruby/test_module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions version.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
10 changes: 6 additions & 4 deletions vm_method.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down

0 comments on commit a3e1444

Please sign in to comment.