Skip to content

Commit 44df509

Browse files
committed
Fix memory leak when exception is raised during JSON generation
If an exception is raised the FBuffer is leaked. For example, the following script leaks memory: o = Object.new def o.to_json(a) = raise 10.times do 100_000.times do begin JSON(o) rescue end end puts `ps -o rss= -p #{$$}` end Before: 31824 35696 40240 44304 47424 50944 54000 58384 62416 65296 After: 24416 24640 24640 24736 24736 24736 24736 24736 24736 24736
1 parent 72d8d40 commit 44df509

File tree

1 file changed

+35
-5
lines changed

1 file changed

+35
-5
lines changed

ext/json/ext/generator/generator.c

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,6 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
892892
struct hash_foreach_arg arg;
893893

894894
if (max_nesting != 0 && depth > max_nesting) {
895-
fbuffer_free(buffer);
896895
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
897896
}
898897
fbuffer_append_char(buffer, '{');
@@ -927,7 +926,6 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
927926
long depth = ++state->depth;
928927
int i, j;
929928
if (max_nesting != 0 && depth > max_nesting) {
930-
fbuffer_free(buffer);
931929
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
932930
}
933931
fbuffer_append_char(buffer, '[');
@@ -1020,10 +1018,8 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
10201018
VALUE tmp = rb_funcall(obj, i_to_s, 0);
10211019
if (!allow_nan) {
10221020
if (isinf(value)) {
1023-
fbuffer_free(buffer);
10241021
rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
10251022
} else if (isnan(value)) {
1026-
fbuffer_free(buffer);
10271023
rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(tmp));
10281024
}
10291025
}
@@ -1096,11 +1092,45 @@ static FBuffer *cState_prepare_buffer(VALUE self)
10961092
return buffer;
10971093
}
10981094

1095+
struct generate_json_data {
1096+
FBuffer *buffer;
1097+
VALUE vstate;
1098+
JSON_Generator_State *state;
1099+
VALUE obj;
1100+
};
1101+
1102+
static VALUE generate_json_try(VALUE d)
1103+
{
1104+
struct generate_json_data *data = (struct generate_json_data *)d;
1105+
1106+
generate_json(data->buffer, data->vstate, data->state, data->obj);
1107+
1108+
return Qnil;
1109+
}
1110+
1111+
static VALUE generate_json_rescue(VALUE d, VALUE exc)
1112+
{
1113+
struct generate_json_data *data = (struct generate_json_data *)d;
1114+
fbuffer_free(data->buffer);
1115+
1116+
rb_exc_raise(exc);
1117+
1118+
return Qundef;
1119+
}
1120+
10991121
static VALUE cState_partial_generate(VALUE self, VALUE obj)
11001122
{
11011123
FBuffer *buffer = cState_prepare_buffer(self);
11021124
GET_STATE(self);
1103-
generate_json(buffer, self, state, obj);
1125+
1126+
struct generate_json_data data = {
1127+
.buffer = buffer,
1128+
.vstate = self,
1129+
.state = state,
1130+
.obj = obj
1131+
};
1132+
rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
1133+
11041134
return fbuffer_to_s(buffer);
11051135
}
11061136

0 commit comments

Comments
 (0)