From 88469daa71430efcdd26d83d1fd0b40a5e3b3dd9 Mon Sep 17 00:00:00 2001 From: Alex Kotov Date: Tue, 14 Jun 2022 13:34:36 +0300 Subject: [PATCH] Ruby: protect from memory leak in case of exception --- bindings/mruby/src/printf.c | 1 + bindings/ruby/ext/default/printf.c | 143 +++++++++++++++++++++++++---- 2 files changed, 124 insertions(+), 20 deletions(-) diff --git a/bindings/mruby/src/printf.c b/bindings/mruby/src/printf.c index 42cc483..27aa7a9 100644 --- a/bindings/mruby/src/printf.c +++ b/bindings/mruby/src/printf.c @@ -2,6 +2,7 @@ #include "dynarg.h" +#include #include #include diff --git a/bindings/ruby/ext/default/printf.c b/bindings/ruby/ext/default/printf.c index ab77a34..1799fe0 100644 --- a/bindings/ruby/ext/default/printf.c +++ b/bindings/ruby/ext/default/printf.c @@ -2,23 +2,77 @@ #include "dynarg.h" +#include +#include + #ifdef KERNAUX_VERSION_WITH_PRINTF +static ID rb_intern_freeze = Qnil; + +/************* + * ::KernAux * + *************/ + +static VALUE rb_KernAux = Qnil; + static VALUE rb_KernAux_snprintf1(int argc, const VALUE *argv, VALUE self); -static ID rb_intern_freeze = Qnil; -static VALUE rb_KernAux = Qnil; +static VALUE rb_KernAux_snprintf1_PROTECT(VALUE userdata); + +/************************ + * ::KernAux::Snprintf1 * + ************************/ + +static VALUE rb_KernAux_Snprintf1 = Qnil; + +static size_t rb_KernAux_Snprintf1_DSIZE(const void *ptr); + +const rb_data_type_t rb_KernAux_Snprintf1_DTYPE = { + .wrap_struct_name = "KernAux::Snprintf1", + .parent = NULL, + .data = NULL, + .flags = RUBY_TYPED_FREE_IMMEDIATELY, + .function = { + .dfree = RUBY_DEFAULT_FREE, + .dsize = rb_KernAux_Snprintf1_DSIZE, + .dmark = NULL, + .dcompact = NULL, + .reserved = { 0 }, + }, +}; + +struct rb_KernAux_Snprintf1_DATA { + const struct KernAux_PrintfFmt_Spec *spec; + const struct DynArg *dynarg; + int size; + const char *format; + char *str; +}; + +/******** + * Main * + ********/ void init_printf() { rb_gc_register_mark_object(ID2SYM(rb_intern_freeze = rb_intern("freeze"))); + rb_gc_register_mark_object(rb_KernAux = rb_define_module("KernAux")); + rb_gc_register_mark_object( + rb_KernAux_Snprintf1 = + // @api private + rb_define_class_under(rb_KernAux, "Snprintf1", rb_cObject)); + rb_funcall(rb_KernAux, rb_intern("private_constant"), 1, ID2SYM(rb_intern("Snprintf1"))); + rb_define_singleton_method(rb_KernAux, "snprintf1", rb_KernAux_snprintf1, -1); } -// FIXME: rewrite to ensure no memory leak on exception. +/************* + * ::KernAux * + *************/ + VALUE rb_KernAux_snprintf1( const int argc, const VALUE *const argv_rb, @@ -82,32 +136,72 @@ VALUE rb_KernAux_snprintf1( char *const str = malloc(size); if (!str) rb_raise(rb_eNoMemError, "snprintf1 buffer malloc"); + struct rb_KernAux_Snprintf1_DATA *userdata; + VALUE userdata_rb = TypedData_Make_Struct( + rb_KernAux_Snprintf1, + struct rb_KernAux_Snprintf1_DATA, + &rb_KernAux_Snprintf1_DTYPE, + userdata + ); + if (NIL_P(userdata_rb) || userdata == NULL) { + free(str); + rb_raise(rb_eNoMemError, "snprintf1 userdata alloc"); + } + + userdata->spec = &spec; + userdata->dynarg = &dynarg; + userdata->size = size; + userdata->format = format; + userdata->str = str; + + int state = 0; + VALUE result = + rb_protect(rb_KernAux_snprintf1_PROTECT, userdata_rb, &state); + + free(str); + + if (state == 0) { + return result; + } else { + rb_jump_tag(state); + } +} + +VALUE rb_KernAux_snprintf1_PROTECT(VALUE userdata_rb) +{ + const struct rb_KernAux_Snprintf1_DATA *userdata = NULL; + TypedData_Get_Struct( + userdata_rb, + struct rb_KernAux_Snprintf1_DATA, + &rb_KernAux_Snprintf1_DTYPE, + userdata + ); + int slen; - if (spec.set_width) { - if (spec.set_precision) { - slen = dynarg.use_dbl - ? kernaux_snprintf(str, size, format, spec.width, spec.precision, dynarg.dbl) - : kernaux_snprintf(str, size, format, spec.width, spec.precision, dynarg.arg); + if (userdata->spec->set_width) { + if (userdata->spec->set_precision) { + slen = userdata->dynarg->use_dbl + ? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->spec->precision, userdata->dynarg->dbl) + : kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->spec->precision, userdata->dynarg->arg); } else { - slen = dynarg.use_dbl - ? kernaux_snprintf(str, size, format, spec.width, dynarg.dbl) - : kernaux_snprintf(str, size, format, spec.width, dynarg.arg); + slen = userdata->dynarg->use_dbl + ? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->dynarg->dbl) + : kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->dynarg->arg); } } else { - if (spec.set_precision) { - slen = dynarg.use_dbl - ? kernaux_snprintf(str, size, format, spec.precision, dynarg.dbl) - : kernaux_snprintf(str, size, format, spec.precision, dynarg.arg); + if (userdata->spec->set_precision) { + slen = userdata->dynarg->use_dbl + ? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->precision, userdata->dynarg->dbl) + : kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->precision, userdata->dynarg->arg); } else { - slen = dynarg.use_dbl - ? kernaux_snprintf(str, size, format, dynarg.dbl) - : kernaux_snprintf(str, size, format, dynarg.arg); + slen = userdata->dynarg->use_dbl + ? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->dynarg->dbl) + : kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->dynarg->arg); } } const VALUE output_rb = - rb_funcall(rb_str_new2(str), rb_intern_freeze, 0); - free(str); + rb_funcall(rb_str_new2(userdata->str), rb_intern_freeze, 0); const VALUE result_rb = rb_ary_new2(2); rb_ary_push(result_rb, output_rb); @@ -115,4 +209,13 @@ VALUE rb_KernAux_snprintf1( return rb_funcall(result_rb, rb_intern_freeze, 0); } +/************************ + * ::KernAux::Snprintf1 * + ************************/ + +size_t rb_KernAux_Snprintf1_DSIZE(const void *const ptr) +{ + return sizeof(struct rb_KernAux_Snprintf1_DATA); +} + #endif // KERNAUX_VERSION_WITH_PRINTF