From 1100288eba30ceb243bcef5a09219c3f9fbda89e Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 24 Aug 2010 04:07:55 +0000 Subject: [PATCH] avoid stack overflow when escaping large strings Attempting to escape large, untrusted strings cause stack overflows (easier under Ruby 1.9 using pthreads) leading to SystemStackError on small overflows and segmentation faults on large overflows. Instead of allocating on the stack, we'll allocate a string buffer from the heap. This has the unfortunate effect of reducing escape performance for common cases, but in my experience SQL escaping isn't much of a bottleneck. For reference, Ruby 1.9.2-p0 with pthreads gets a stack size of 512K and the default process stack size is 8MB on both x86 and x86_64 Linux. --- ext/mysql2/client.c | 6 +++--- spec/mysql2/client_spec.rb | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ext/mysql2/client.c b/ext/mysql2/client.c index c6a9dab..f6c947a 100644 --- a/ext/mysql2/client.c +++ b/ext/mysql2/client.c @@ -342,15 +342,15 @@ static VALUE rb_mysql_client_escape(VALUE self, VALUE str) { #endif oldLen = RSTRING_LEN(str); - char escaped[(oldLen*2)+1]; + newStr = rb_str_new(0, oldLen*2+1); REQUIRE_OPEN_DB(client); - newLen = mysql_real_escape_string(client, escaped, StringValuePtr(str), oldLen); + newLen = mysql_real_escape_string(client, RSTRING_PTR(newStr), StringValuePtr(str), oldLen); if (newLen == oldLen) { // no need to return a new ruby string if nothing changed return str; } else { - newStr = rb_str_new(escaped, newLen); + rb_str_resize(newStr, newLen); #ifdef HAVE_RUBY_ENCODING_H rb_enc_associate(newStr, conn_enc); if (default_internal_enc) { diff --git a/spec/mysql2/client_spec.rb b/spec/mysql2/client_spec.rb index b3ffcbc..936e1ea 100644 --- a/spec/mysql2/client_spec.rb +++ b/spec/mysql2/client_spec.rb @@ -144,6 +144,18 @@ describe Mysql2::Client do @client.escape(str).object_id.should eql(str.object_id) end + it "#escape should not overflow the thread stack" do + lambda { + Thread.new { @client.escape("'" * 256 * 1024) }.join + }.should_not raise_error(SystemStackError) + end + + it "#escape should not overflow the process stack" do + lambda { + Thread.new { @client.escape("'" * 1024 * 1024 * 4) }.join + }.should_not raise_error(SystemStackError) + end + it "should respond to #info" do @client.should respond_to(:info) end