summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2025-10-31 23:52:23 +0900
committergit <svn-admin@ruby-lang.org>2025-10-31 16:02:26 +0000
commit6bdb2027f64df8e6f59273488ea01ed1614bcf76 (patch)
treefd0bc93a3aa810ac8c9a43fca9f490292e3ba7f2
parent65168b7eafd30fd3958d71761fa155bba00cdec5 (diff)
[ruby/json] Fix memory leak when exception is raised during JSON generation part 2
Commit https://github.com/ruby/json/commit/44df509dc2de fixed it for StandardError, but other exceptions and jumps are also possible. Use rb_ensure() to release FBuffer instead of rb_rescue(). A reproducer: o = Object.new def o.to_json(a) = throw :a a = ["make heap allocation"*100, o] 10.times do 100_000.times do catch(:a) { JSON(a) } end puts `ps -o rss= -p #{$$}` end https://github.com/ruby/json/commit/9b7b648ecd
-rw-r--r--ext/json/fbuffer/fbuffer.h5
-rw-r--r--ext/json/generator/generator.c18
2 files changed, 6 insertions, 17 deletions
diff --git a/ext/json/fbuffer/fbuffer.h b/ext/json/fbuffer/fbuffer.h
index d5fd8ac6d7..7d57a87b14 100644
--- a/ext/json/fbuffer/fbuffer.h
+++ b/ext/json/fbuffer/fbuffer.h
@@ -283,13 +283,10 @@ static VALUE fbuffer_finalize(FBuffer *fb)
{
if (fb->io) {
fbuffer_flush(fb);
- fbuffer_free(fb);
rb_io_flush(fb->io);
return fb->io;
} else {
- VALUE result = rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
- fbuffer_free(fb);
- return result;
+ return rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
}
}
diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c
index d04c8a9079..72155abe52 100644
--- a/ext/json/generator/generator.c
+++ b/ext/json/generator/generator.c
@@ -1444,16 +1444,14 @@ static VALUE generate_json_try(VALUE d)
data->func(data->buffer, data, data->obj);
- return Qnil;
+ return fbuffer_finalize(data->buffer);
}
-static VALUE generate_json_rescue(VALUE d, VALUE exc)
+static VALUE generate_json_ensure(VALUE d)
{
struct generate_json_data *data = (struct generate_json_data *)d;
fbuffer_free(data->buffer);
- rb_exc_raise(exc);
-
return Qundef;
}
@@ -1474,9 +1472,7 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func,
.obj = obj,
.func = func
};
- rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
-
- return fbuffer_finalize(&buffer);
+ return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
}
/* call-seq:
@@ -1522,9 +1518,7 @@ static VALUE cState_generate_new(int argc, VALUE *argv, VALUE self)
.obj = obj,
.func = generate_json
};
- rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
-
- return fbuffer_finalize(&buffer);
+ return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
}
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
@@ -2030,9 +2024,7 @@ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
.obj = obj,
.func = generate_json,
};
- rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
-
- return fbuffer_finalize(&buffer);
+ return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
}
/*