Fix memory leak in parser for incomplete tokens

[Bug #19835]

The parser does not free the `tbl` of the `struct vtable` when there are
leftover `lvtbl` in the parser. This causes a memory leak.

The following script reproduces this issue:

```
10.times do
  100_000.times do
    Ripper.parse("class Foo")
  end

  puts `ps -o rss= -p #{$$}`
end
```
This commit is contained in:
Peter Zhu 2023-08-09 11:03:13 -04:00
parent d3efce69ea
commit 5bc8fceca8
Notes: git 2023-08-09 18:07:18 +00:00
2 changed files with 35 additions and 14 deletions

42
parse.y
View File

@ -13228,27 +13228,40 @@ local_push(struct parser_params *p, int toplevel_scope)
p->lvtbl = local;
}
static void
local_free(struct parser_params *p, struct local_vars *local)
{
if (local->used) {
vtable_free(local->used);
}
# if WARN_PAST_SCOPE
while (local->past) {
struct vtable *past = local->past;
local->past = past->prev;
vtable_free(past);
}
# endif
vtable_free(local->args);
vtable_free(local->vars);
ruby_sized_xfree(local, sizeof(struct local_vars));
}
static void
local_pop(struct parser_params *p)
{
struct local_vars *local = p->lvtbl->prev;
if (p->lvtbl->used) {
warn_unused_var(p, p->lvtbl);
vtable_free(p->lvtbl->used);
}
# if WARN_PAST_SCOPE
while (p->lvtbl->past) {
struct vtable *past = p->lvtbl->past;
p->lvtbl->past = past->prev;
vtable_free(past);
}
# endif
vtable_free(p->lvtbl->args);
vtable_free(p->lvtbl->vars);
local_free(p, p->lvtbl);
p->lvtbl = local;
CMDARG_POP();
COND_POP();
ruby_sized_xfree(p->lvtbl, sizeof(*p->lvtbl));
p->lvtbl = local;
}
#ifndef RIPPER
@ -13856,11 +13869,12 @@ rb_ruby_parser_free(void *ptr)
if (p->tokenbuf) {
ruby_sized_xfree(p->tokenbuf, p->toksiz);
}
for (local = p->lvtbl; local; local = prev) {
xfree(local->vars);
prev = local->prev;
xfree(local);
local_free(p, local);
}
{
token_info *ptinfo;
while ((ptinfo = p->token_info) != 0) {

View File

@ -154,6 +154,13 @@ end
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;
end
class TestInput < self