summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornagachika <nagachika@ruby-lang.org>2023-08-13 13:35:25 +0900
committernagachika <nagachika@ruby-lang.org>2023-08-13 13:35:25 +0900
commit0c908fa681271f13750aa64420203f1a58fa03fe (patch)
treec481602fa506844e914b0e95353f11d73ec85386
parent6898389a0f640c4155a7073579f43d1e16893698 (diff)
merge revision(s) 0b8f15575a440f85ac686f5b0eae8f8b7c2b72e7: [Backport #19836]
Fix memory leak for incomplete lambdas [Bug #19836] The parser does not free the chain of `struct vtable`, which causes memory leaks. The following script reproduces this issue: ``` 10.times do 100_000.times do Ripper.parse("-> {") end puts `ps -o rss= -p #{$$}` end ``` --- parse.y | 24 ++++++++++++++---------- test/ripper/test_ripper.rb | 7 +++++++ 2 files changed, 21 insertions(+), 10 deletions(-)
-rw-r--r--parse.y24
-rw-r--r--test/ripper/test_ripper.rb29
-rw-r--r--version.h2
3 files changed, 37 insertions, 18 deletions
diff --git a/parse.y b/parse.y
index 221b6b7271..9dd56c0af1 100644
--- a/parse.y
+++ b/parse.y
@@ -13176,22 +13176,26 @@ local_push(struct parser_params *p, int toplevel_scope)
}
static void
-local_free(struct parser_params *p, struct local_vars *local)
+vtable_chain_free(struct parser_params *p, struct vtable *table)
{
- if (local->used) {
- vtable_free(local->used);
+ while (!DVARS_TERMINAL_P(table)) {
+ struct vtable *cur_table = table;
+ table = cur_table->prev;
+ vtable_free(cur_table);
}
+}
+
+static void
+local_free(struct parser_params *p, struct local_vars *local)
+{
+ vtable_chain_free(p, local->used);
# if WARN_PAST_SCOPE
- while (local->past) {
- struct vtable *past = local->past;
- local->past = past->prev;
- vtable_free(past);
- }
+ vtable_chain_free(p, local->past);
# endif
- vtable_free(local->args);
- vtable_free(local->vars);
+ vtable_chain_free(p, local->args);
+ vtable_chain_free(p, local->vars);
ruby_sized_xfree(local, sizeof(struct local_vars));
}
diff --git a/test/ripper/test_ripper.rb b/test/ripper/test_ripper.rb
index a9773a89d8..83b43d67fe 100644
--- a/test/ripper/test_ripper.rb
+++ b/test/ripper/test_ripper.rb
@@ -120,13 +120,6 @@ class TestRipper::Ripper < Test::Unit::TestCase
END
assert_nil(Ripper.sexp(src), bug12651)
end;
-
- # [Bug #19835]
- assert_no_memory_leak(%w(-rripper), "", "#{<<~'end;'}", rss: true)
- 1_000_000.times do
- Ripper.parse("class Foo")
- end
- end;
end
# https://bugs.jruby.org/4176
@@ -148,6 +141,28 @@ end
assert_nothing_raised { Ripper.lex src }
end
+ def test_no_memory_leak
+ assert_no_memory_leak(%w(-rripper), "", "#{<<~'end;'}", rss: true)
+ 2_000_000.times do
+ Ripper.parse("")
+ end
+ end;
+
+ # [Bug #19835]
+ assert_no_memory_leak(%w(-rripper), "", "#{<<~'end;'}", rss: true)
+ 1_000_000.times do
+ Ripper.parse("class Foo")
+ end
+ end;
+
+ # [Bug #19836]
+ assert_no_memory_leak(%w(-rripper), "", "#{<<~'end;'}", rss: true)
+ 1_000_000.times do
+ Ripper.parse("-> {")
+ end
+ end;
+ end
+
class TestInput < self
Input = Struct.new(:lines) do
def gets
diff --git a/version.h b/version.h
index 875f6a20db..8235f15c3e 100644
--- a/version.h
+++ b/version.h
@@ -11,7 +11,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 2
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
-#define RUBY_PATCHLEVEL 112
+#define RUBY_PATCHLEVEL 113
#include "ruby/version.h"
#include "ruby/internal/abi.h"