From 0b8f15575a440f85ac686f5b0eae8f8b7c2b72e7 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Wed, 9 Aug 2023 12:18:28 -0400 Subject: [PATCH] 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(-) diff --git a/parse.y b/parse.y index 0909356f33..c1c8a7514b 100644 --- a/parse.y +++ b/parse.y @@ -13228,23 +13228,27 @@ local_push(struct parser_params *p, int toplevel_scope) p->lvtbl = local; } +static void +vtable_chain_free(struct parser_params *p, struct vtable *table) +{ + 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) { - if (local->used) { - vtable_free(local->used); - } + 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