From f948f359b71d908e362bafa6bdad95b3398c339c Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Wed, 16 Nov 2016 21:48:11 +0900 Subject: Make Psych::Emitter store the IO object in an instance variable The IO object given to Psych::Emitter#initialize is saved inside LibYAML, yaml_emitter_t. This is a problem because Ruby's GC marker can't find it, and can lead to use after free. So, store the IO object in an instance variable and fetch from it every time the write handler is called. The segmentation fault can be reproduced with the following snippet: emitter = Psych::Emitter.new(open("/dev/null", "w")) GC.start emitter.start_stream(Psych::Parser::UTF16BE) # make it write something emitter.end_stream --- ext/psych/psych_emitter.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ext/psych/psych_emitter.c b/ext/psych/psych_emitter.c index 371c285..52d1b70 100644 --- a/ext/psych/psych_emitter.c +++ b/ext/psych/psych_emitter.c @@ -8,6 +8,7 @@ #endif VALUE cPsychEmitter; +static ID id_io; static ID id_write; static ID id_line_width; static ID id_indentation; @@ -21,7 +22,7 @@ static void emit(yaml_emitter_t * emitter, yaml_event_t * event) static int writer(void *ctx, unsigned char *buffer, size_t size) { - VALUE io = (VALUE)ctx; + VALUE self = (VALUE)ctx, io = rb_attr_get(self, id_io); #ifdef HAVE_RUBY_ENCODING_H VALUE str = rb_enc_str_new((const char *)buffer, (long)size, rb_utf8_encoding()); #else @@ -94,7 +95,8 @@ static VALUE initialize(int argc, VALUE *argv, VALUE self) yaml_emitter_set_canonical(emitter, Qtrue == canonical ? 1 : 0); } - yaml_emitter_set_output(emitter, writer, (void *)io); + rb_ivar_set(self, id_io, io); + yaml_emitter_set_output(emitter, writer, (void *)self); return self; } @@ -562,6 +564,7 @@ void Init_psych_emitter(void) rb_define_method(cPsychEmitter, "line_width", line_width, 0); rb_define_method(cPsychEmitter, "line_width=", set_line_width, 1); + id_io = rb_intern("io"); id_write = rb_intern("write"); id_line_width = rb_intern("line_width"); id_indentation = rb_intern("indentation"); -- cgit v1.2.1