Skip to content

Commit 021c554

Browse files
authored
Fix test_no_memory_leak failure (#424)
Avoid memory leak when TypedData_Wrap_Struct fail. First, prepare TypedData_Wrap_Struct with NULL. Next, allocate Real. Finally, bind VALUE and Real.
1 parent d9af278 commit 021c554

File tree

1 file changed

+21
-6
lines changed

1 file changed

+21
-6
lines changed

ext/bigdecimal/bigdecimal.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,21 @@ static const rb_data_type_t BigDecimal_data_type = {
252252
#endif
253253
};
254254

255+
// TypedData_Wrap_Struct may fail if there is no memory, or GC.add_stress_to_class(BigDecimal) is set.
256+
// We need to first allocate empty struct, allocate Real struct, and then set the data pointer.
257+
typedef struct { VALUE _obj; } NULL_WRAPPED_VALUE;
258+
static NULL_WRAPPED_VALUE
259+
BigDecimal_alloc_empty_struct(VALUE klass)
260+
{
261+
return (NULL_WRAPPED_VALUE) { TypedData_Wrap_Struct(klass, &BigDecimal_data_type, NULL) };
262+
}
263+
255264
static VALUE
256-
BigDecimal_wrap_struct(VALUE klass, Real *real)
265+
BigDecimal_wrap_struct(NULL_WRAPPED_VALUE v, Real *real)
257266
{
258-
VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, real);
267+
VALUE obj = v._obj;
268+
assert(RTYPEDDATA_DATA(obj) == NULL);
269+
RTYPEDDATA_DATA(obj) = real;
259270
RB_OBJ_FREEZE(obj);
260271
return obj;
261272
}
@@ -265,8 +276,9 @@ MAYBE_UNUSED(static inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_
265276
static BDVALUE
266277
rbd_allocate_struct_zero_wrap(int sign, size_t const digits)
267278
{
279+
NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
268280
Real *real = rbd_allocate_struct_zero(sign, digits);
269-
return (BDVALUE) { BigDecimal_wrap_struct(rb_cBigDecimal, real), real };
281+
return (BDVALUE) { BigDecimal_wrap_struct(null_wrapped, real), real };
270282
}
271283

272284
static inline int
@@ -1041,9 +1053,10 @@ check_int_precision(VALUE v)
10411053
static NULLABLE_BDVALUE
10421054
CreateFromString(const char *str, VALUE klass, bool strict_p, bool raise_exception)
10431055
{
1056+
NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(klass);
10441057
Real *pv = VpAlloc(str, strict_p, raise_exception);
10451058
if (!pv) return (NULLABLE_BDVALUE) { Qnil, NULL };
1046-
return (NULLABLE_BDVALUE) { BigDecimal_wrap_struct(klass, pv), pv };
1059+
return (NULLABLE_BDVALUE) { BigDecimal_wrap_struct(null_wrapped, pv), pv };
10471060
}
10481061

10491062
static Real *
@@ -2580,6 +2593,7 @@ check_exception(VALUE bd)
25802593
static VALUE
25812594
rb_uint64_convert_to_BigDecimal(uint64_t uval)
25822595
{
2596+
NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
25832597
Real *vp;
25842598
if (uval == 0) {
25852599
vp = rbd_allocate_struct(1);
@@ -2621,7 +2635,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval)
26212635
MEMCPY(vp->frac, buf + BIGDECIMAL_INT64_MAX_LENGTH - len, DECDIG, len);
26222636
}
26232637

2624-
return BigDecimal_wrap_struct(rb_cBigDecimal, vp);
2638+
return BigDecimal_wrap_struct(null_wrapped, vp);
26252639
}
26262640

26272641
static VALUE
@@ -2905,12 +2919,13 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
29052919
if (digs == SIZE_MAX)
29062920
return check_exception(val);
29072921

2922+
NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
29082923
Real *vp;
29092924
TypedData_Get_Struct(val, Real, &BigDecimal_data_type, vp);
29102925
vp = VpCopy(NULL, vp);
29112926
RB_GC_GUARD(val);
29122927

2913-
VALUE copy = BigDecimal_wrap_struct(rb_cBigDecimal, vp);
2928+
VALUE copy = BigDecimal_wrap_struct(null_wrapped, vp);
29142929
/* TODO: rounding */
29152930
return check_exception(copy);
29162931
}

0 commit comments

Comments
 (0)