From 69a43a160339277aee02d4967b048f7cf5d4d345 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 21 Aug 2026 10:59:22 +0900 Subject: [PATCH] Fix SyckParser leak in Syck.compile rb_syck_compile freed the parser only at the end of the function, so the rb_raise for a missing root node leaked it, as would NoMemoryError from the result string allocation. Free the parser before raising, and take ownership of the bytecode buffer so the parser can be freed before building the Ruby string. Co-Authored-By: Claude Fable 5 --- ext/syck/rubyext.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/ext/syck/rubyext.c b/ext/syck/rubyext.c index 3c31e74..d16316b 100644 --- a/ext/syck/rubyext.c +++ b/ext/syck/rubyext.c @@ -149,7 +149,6 @@ rb_syck_compile(VALUE self, VALUE port) int taint; char *ret; long blen; - VALUE ret_v; VALUE bc; bytestring_t *sav = NULL; void *data = NULL; @@ -162,19 +161,25 @@ rb_syck_compile(VALUE self, VALUE port) syck_parser_taguri_expansion( parser, 0 ); oid = syck_parse( parser ); if (!syck_lookup_sym( parser, oid, &data )) { + syck_free_parser( parser ); rb_raise(rb_eSyntaxError, "root node <%p> not found", (void *)oid); } sav = data; - blen = (long)strlen( sav->buffer ); - ret = ALLOCV_N( char, ret_v, blen + 3 ); - memcpy( ret, "D\n", 2 ); - memcpy( ret + 2, sav->buffer, (size_t)blen + 1 ); - + /* + * Steal the buffer from the parser's symbol table (S_FREE ignores + * the NULLed pointer) so the parser can be freed before rb_str_new, + * which may raise and would otherwise leak the parser. + */ + ret = sav->buffer; + sav->buffer = NULL; syck_free_parser( parser ); - bc = rb_str_new( ret, blen + 2 ); - ALLOCV_END( ret_v ); + blen = (long)strlen( ret ); + bc = rb_str_new( 0, blen + 2 ); + memcpy( RSTRING_PTR(bc), "D\n", 2 ); + memcpy( RSTRING_PTR(bc) + 2, ret, (size_t)blen ); + S_FREE( ret ); if ( taint ) OBJ_TAINT( bc ); return bc; }