diff --git a/ext/syck/rubyext.c b/ext/syck/rubyext.c index 4d7f7e2..3c31e74 100644 --- a/ext/syck/rubyext.c +++ b/ext/syck/rubyext.c @@ -148,6 +148,8 @@ rb_syck_compile(VALUE self, VALUE port) SYMID oid; int taint; char *ret; + long blen; + VALUE ret_v; VALUE bc; bytestring_t *sav = NULL; void *data = NULL; @@ -164,14 +166,15 @@ rb_syck_compile(VALUE self, VALUE port) } sav = data; - ret = S_ALLOCA_N( char, strlen( sav->buffer ) + 3 ); - ret[0] = '\0'; - strcat( ret, "D\n" ); - strcat( ret, sav->buffer ); + 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 ); syck_free_parser( parser ); - bc = rb_str_new2( ret ); + bc = rb_str_new( ret, blen + 2 ); + ALLOCV_END( ret_v ); if ( taint ) OBJ_TAINT( bc ); return bc; } @@ -1213,13 +1216,17 @@ syck_set_ivars( ) { VALUE ivname = rb_ary_entry( vars, 0 ); + VALUE ivn_v; char *ivn; + long ivn_len; StringValue( ivname ); - ivn = S_ALLOCA_N( char, RSTRING_LEN(ivname) + 2 ); + ivn_len = RSTRING_LEN(ivname); + ivn = ALLOCV_N( char, ivn_v, ivn_len + 2 ); ivn[0] = '@'; - ivn[1] = '\0'; - strncat( ivn, RSTRING_PTR(ivname), RSTRING_LEN(ivname) ); + memcpy( ivn + 1, RSTRING_PTR(ivname), (size_t)ivn_len ); + ivn[ivn_len + 1] = '\0'; rb_iv_set( obj, ivn, rb_ary_entry( vars, 1 ) ); + ALLOCV_END( ivn_v ); return Qnil; } diff --git a/test/test_large_input.rb b/test/test_large_input.rb new file mode 100644 index 0000000..e66f49e --- /dev/null +++ b/test/test_large_input.rb @@ -0,0 +1,26 @@ +require 'helper' + +module Syck + # Both of these sized an `alloca` from the document. They are run on a Thread + # because a Ruby thread's stack is far smaller than the main stack, which is + # the shape a web or job worker actually has. + class TestLargeInput < Test::Unit::TestCase + SIZE = 4 * 1024 * 1024 + + def on_thread + Thread.new { yield }.value + end + + def test_long_ivar_name + doc = "--- !ruby/object:Object\n" + ('n' * SIZE) + ": 1\n" + obj = on_thread { Syck.load(doc) } + assert_equal 1, obj.instance_variable_get("@#{'n' * SIZE}") + end + + def test_long_compile_input + doc = "--- \n" + (0...(SIZE / 20)).map { |i| "k#{i}: v#{i}\n" }.join + bc = on_thread { Syck.compile(doc) } + assert_equal "D\n", bc[0, 2] + end + end +end