diff options
author | Peter Zhu <peter@peterzhu.ca> | 2023-08-09 11:03:13 -0400 |
---|---|---|
committer | Peter Zhu <peter@peterzhu.ca> | 2023-08-09 14:06:58 -0400 |
commit | 5bc8fceca8d47ed1ef9c603c6531a408de36b60c (patch) | |
tree | e0369c5b3b2edbccc5c656241ca885b01e149109 /parse.y | |
parent | d3efce69eaabf1ff81bcdf3631350a87ac0dda28 (diff) |
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
```
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/8192
Diffstat (limited to 'parse.y')
-rw-r--r-- | parse.y | 42 |
1 files changed, 28 insertions, 14 deletions
@@ -13229,26 +13229,39 @@ local_push(struct parser_params *p, int toplevel_scope) } static void -local_pop(struct parser_params *p) +local_free(struct parser_params *p, struct local_vars *local) { - struct local_vars *local = p->lvtbl->prev; - if (p->lvtbl->used) { - warn_unused_var(p, p->lvtbl); - vtable_free(p->lvtbl->used); + if (local->used) { + vtable_free(local->used); } + # if WARN_PAST_SCOPE - while (p->lvtbl->past) { - struct vtable *past = p->lvtbl->past; - p->lvtbl->past = past->prev; + while (local->past) { + struct vtable *past = local->past; + local->past = past->prev; vtable_free(past); } # endif - vtable_free(p->lvtbl->args); - vtable_free(p->lvtbl->vars); + + 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); + } + + 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) { |