summaryrefslogtreecommitdiff
path: root/parse.y
diff options
context:
space:
mode:
authornagachika <nagachika@ruby-lang.org>2023-08-13 13:21:30 +0900
committernagachika <nagachika@ruby-lang.org>2023-08-13 13:21:30 +0900
commit6898389a0f640c4155a7073579f43d1e16893698 (patch)
tree5de77c056251d4161120354da7ba891dfa9cad9f /parse.y
parentead1a83b2f5951cd09667ed2722ab14b9bcf0834 (diff)
merge revision(s) 5bc8fceca8d47ed1ef9c603c6531a408de36b60c: [Backport #19835]
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 ``` --- parse.y | 42 ++++++++++++++++++++++++++++-------------- test/ripper/test_ripper.rb | 7 +++++++ 2 files changed, 35 insertions(+), 14 deletions(-)
Diffstat (limited to 'parse.y')
-rw-r--r--parse.y54
1 files changed, 34 insertions, 20 deletions
diff --git a/parse.y b/parse.y
index 1a27175688..221b6b7271 100644
--- a/parse.y
+++ b/parse.y
@@ -13176,26 +13176,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;
- vtable_free(past);
+ 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
@@ -13776,16 +13789,17 @@ parser_free(void *ptr)
if (p->tokenbuf) {
ruby_sized_xfree(p->tokenbuf, p->toksiz);
}
+
for (local = p->lvtbl; local; local = prev) {
- if (local->vars) xfree(local->vars);
- prev = local->prev;
- xfree(local);
+ prev = local->prev;
+ local_free(p, local);
}
+
{
- token_info *ptinfo;
- while ((ptinfo = p->token_info) != 0) {
- p->token_info = ptinfo->next;
- xfree(ptinfo);
+ token_info *ptinfo;
+ while ((ptinfo = p->token_info) != 0) {
+ p->token_info = ptinfo->next;
+ xfree(ptinfo);
}
}
xfree(ptr);