Skip to content

Commit 5643df6

Browse files
committed
Fix a memory leak in #to_json methods
Fix: ruby#460 The various `to_json` methods must rescue exceptions to free the buffer. ``` require 'json' data = 10_000.times.to_a << BasicObject.new 20.times do 100.times do begin data.to_json rescue NoMethodError end end puts `ps -o rss= -p #{$$}` end ``` ``` 20128 24992 29920 34672 39600 44336 49136 53936 58816 63616 68416 73232 78032 82896 87696 92528 97408 102208 107008 111808 ```
1 parent 790912f commit 5643df6

File tree

2 files changed

+36
-28
lines changed

2 files changed

+36
-28
lines changed

ext/json/ext/generator/generator.c

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,9 @@ static char *fstrndup(const char *ptr, unsigned long len) {
397397
*/
398398
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
399399
{
400-
GENERATE_JSON(object);
400+
rb_check_arity(argc, 0, 1);
401+
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
402+
return cState_partial_generate(Vstate, self, generate_json_object);
401403
}
402404

403405
/*
@@ -409,7 +411,9 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
409411
* produced JSON string output further.
410412
*/
411413
static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
412-
GENERATE_JSON(array);
414+
rb_check_arity(argc, 0, 1);
415+
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
416+
return cState_partial_generate(Vstate, self, generate_json_array);
413417
}
414418

415419
#ifdef RUBY_INTEGER_UNIFICATION
@@ -420,7 +424,9 @@ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
420424
*/
421425
static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
422426
{
423-
GENERATE_JSON(integer);
427+
rb_check_arity(argc, 0, 1);
428+
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
429+
return cState_partial_generate(Vstate, self, generate_json_integer);
424430
}
425431

426432
#else
@@ -431,7 +437,9 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
431437
*/
432438
static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self)
433439
{
434-
GENERATE_JSON(fixnum);
440+
rb_check_arity(argc, 0, 1);
441+
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
442+
return cState_partial_generate(Vstate, self, generate_json_fixnum);
435443
}
436444

437445
/*
@@ -441,7 +449,9 @@ static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self)
441449
*/
442450
static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
443451
{
444-
GENERATE_JSON(bignum);
452+
rb_check_arity(argc, 0, 1);
453+
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
454+
return cState_partial_generate(Vstate, self, generate_json_bignum);
445455
}
446456
#endif
447457

@@ -452,7 +462,9 @@ static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
452462
*/
453463
static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
454464
{
455-
GENERATE_JSON(float);
465+
rb_check_arity(argc, 0, 1);
466+
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
467+
return cState_partial_generate(Vstate, self, generate_json_float);
456468
}
457469

458470
/*
@@ -475,7 +487,9 @@ static VALUE mString_included_s(VALUE self, VALUE modul) {
475487
*/
476488
static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
477489
{
478-
GENERATE_JSON(string);
490+
rb_check_arity(argc, 0, 1);
491+
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
492+
return cState_partial_generate(Vstate, self, generate_json_string);
479493
}
480494

481495
/*
@@ -530,7 +544,8 @@ static VALUE mString_Extend_json_create(VALUE self, VALUE o)
530544
*/
531545
static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
532546
{
533-
GENERATE_JSON(true);
547+
rb_check_arity(argc, 0, 1);
548+
return rb_utf8_str_new("true", 4);
534549
}
535550

536551
/*
@@ -540,7 +555,8 @@ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
540555
*/
541556
static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
542557
{
543-
GENERATE_JSON(false);
558+
rb_check_arity(argc, 0, 1);
559+
return rb_utf8_str_new("false", 5);
544560
}
545561

546562
/*
@@ -550,7 +566,8 @@ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
550566
*/
551567
static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
552568
{
553-
GENERATE_JSON(null);
569+
rb_check_arity(argc, 0, 1);
570+
return rb_utf8_str_new("null", 4);
554571
}
555572

556573
/*
@@ -567,7 +584,7 @@ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
567584
rb_scan_args(argc, argv, "01", &state);
568585
Check_Type(string, T_STRING);
569586
state = cState_from_state_s(cState, state);
570-
return cState_partial_generate(state, string);
587+
return cState_partial_generate(state, string, generate_json_string);
571588
}
572589

573590
static void State_free(void *ptr)
@@ -834,6 +851,7 @@ static void generate_json_integer(FBuffer *buffer, VALUE Vstate, JSON_Generator_
834851
generate_json_bignum(buffer, Vstate, state, obj);
835852
}
836853
#endif
854+
837855
static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
838856
{
839857
double value = RFLOAT_VALUE(obj);
@@ -910,13 +928,14 @@ struct generate_json_data {
910928
VALUE vstate;
911929
JSON_Generator_State *state;
912930
VALUE obj;
931+
void (*func)(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
913932
};
914933

915934
static VALUE generate_json_try(VALUE d)
916935
{
917936
struct generate_json_data *data = (struct generate_json_data *)d;
918937

919-
generate_json(data->buffer, data->vstate, data->state, data->obj);
938+
data->func(data->buffer, data->vstate, data->state, data->obj);
920939

921940
return Qnil;
922941
}
@@ -931,7 +950,7 @@ static VALUE generate_json_rescue(VALUE d, VALUE exc)
931950
return Qundef;
932951
}
933952

934-
static VALUE cState_partial_generate(VALUE self, VALUE obj)
953+
static VALUE cState_partial_generate(VALUE self, VALUE obj, void (*func)(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj))
935954
{
936955
GET_STATE(self);
937956

@@ -942,7 +961,8 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj)
942961
.buffer = &buffer,
943962
.vstate = self,
944963
.state = state,
945-
.obj = obj
964+
.obj = obj,
965+
.func = func
946966
};
947967
rb_rescue(generate_json_try, (VALUE)&data, generate_json_rescue, (VALUE)&data);
948968

@@ -958,7 +978,7 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj)
958978
*/
959979
static VALUE cState_generate(VALUE self, VALUE obj)
960980
{
961-
VALUE result = cState_partial_generate(self, obj);
981+
VALUE result = cState_partial_generate(self, obj, generate_json);
962982
GET_STATE(self);
963983
(void)state;
964984
return result;

ext/json/ext/generator/generator.h

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,6 @@ typedef struct JSON_Generator_StateStruct {
5454
JSON_Generator_State *state; \
5555
GET_STATE_TO(self, state)
5656

57-
#define GENERATE_JSON(type) \
58-
VALUE Vstate; \
59-
JSON_Generator_State *state; \
60-
\
61-
rb_scan_args(argc, argv, "01", &Vstate); \
62-
Vstate = cState_from_state_s(cState, Vstate); \
63-
TypedData_Get_Struct(Vstate, JSON_Generator_State, &JSON_Generator_State_type, state); \
64-
FBuffer buffer = {0}; \
65-
fbuffer_init(&buffer, state->buffer_initial_length); \
66-
generate_json_##type(&buffer, Vstate, state, self); \
67-
return fbuffer_to_s(&buffer)
68-
6957
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self);
7058
static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self);
7159
#ifdef RUBY_INTEGER_UNIFICATION
@@ -99,7 +87,7 @@ static void generate_json_integer(FBuffer *buffer, VALUE Vstate, JSON_Generator_
9987
static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
10088
static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
10189
static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj);
102-
static VALUE cState_partial_generate(VALUE self, VALUE obj);
90+
static VALUE cState_partial_generate(VALUE self, VALUE obj, void (*func)(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj));
10391
static VALUE cState_generate(VALUE self, VALUE obj);
10492
static VALUE cState_from_state_s(VALUE self, VALUE opts);
10593
static VALUE cState_indent(VALUE self);

0 commit comments

Comments
 (0)