summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Zhu <[email protected]>2023-08-09 12:18:28 -0400
committerPeter Zhu <[email protected]>2023-08-09 14:06:58 -0400
commit0b8f15575a440f85ac686f5b0eae8f8b7c2b72e7 (patch)
treef0435bad733992270e673308cd59a04d0373e895
parent5bc8fceca8d47ed1ef9c603c6531a408de36b60c (diff)
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 ```
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/8192
-rw-r--r--parse.y24
-rw-r--r--test/ripper/test_ripper.rb7
2 files changed, 21 insertions, 10 deletions
diff --git a/parse.y b/parse.y
index 0909356f33..c1c8a7514b 100644
--- a/parse.y
+++ b/parse.y
@@ -13229,22 +13229,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 25ddc381cc..9adfd97dfd 100644
--- a/test/ripper/test_ripper.rb
+++ b/test/ripper/test_ripper.rb
@@ -161,6 +161,13 @@ end
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