diff --git a/ChangeLog b/ChangeLog index 676c0edc20..0eec296dcf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Thu Feb 19 15:32:26 2015 Dave Stevens + + * string.c (str_make_independent_expand): terminate String when + moved from heap to embedded. [Fix GH-821]. + Thu Feb 19 13:35:21 2015 Nobuyoshi Nakada * ext/sdbm/_sdbm.c: include ruby/ruby.h for PRIdPTRDIFF when a diff --git a/string.c b/string.c index 0fae07d938..e857e3c35f 100644 --- a/string.c +++ b/string.c @@ -98,6 +98,9 @@ VALUE rb_cSymbol; #define RESIZE_CAPA(str,capacity) do {\ const int termlen = TERM_LEN(str);\ + RESIZE_CAPA_TERM(str,capacity,termlen);\ +} while (0) +#define RESIZE_CAPA_TERM(str,capacity,termlen) do {\ if (STR_EMBED_P(str)) {\ if ((capacity) > RSTRING_EMBED_LEN_MAX) {\ char *const tmp = ALLOC_N(char, (capacity)+termlen);\ @@ -1589,10 +1592,11 @@ str_make_independent_expand(VALUE str, long expand) if (len > capa) len = capa; - if (capa <= RSTRING_EMBED_LEN_MAX && !STR_EMBED_P(str)) { + if (capa + termlen - 1 <= RSTRING_EMBED_LEN_MAX && !STR_EMBED_P(str)) { ptr = RSTRING(str)->as.heap.ptr; STR_SET_EMBED(str); memcpy(RSTRING(str)->as.ary, ptr, len); + TERM_FILL(RSTRING(str)->as.ary + len, termlen); STR_SET_EMBED_LEN(str, len); return; } @@ -2166,23 +2170,30 @@ rb_str_resize(VALUE str, long len) static VALUE str_buf_cat(VALUE str, const char *ptr, long len) { - long capa, total, off = -1; + long capa, total, olen, off = -1; + char *sptr; + const int termlen = TERM_LEN(str); - if (ptr >= RSTRING_PTR(str) && ptr <= RSTRING_END(str)) { - off = ptr - RSTRING_PTR(str); + RSTRING_GETMEM(str, sptr, olen); + if (ptr >= sptr && ptr <= sptr + olen) { + off = ptr - sptr; } rb_str_modify(str); if (len == 0) return 0; if (STR_EMBED_P(str)) { capa = RSTRING_EMBED_LEN_MAX; + sptr = RSTRING(str)->as.ary; + olen = RSTRING_EMBED_LEN(str); } else { capa = RSTRING(str)->as.heap.aux.capa; + sptr = RSTRING(str)->as.heap.ptr; + olen = RSTRING(str)->as.heap.len; } - if (RSTRING_LEN(str) >= LONG_MAX - len) { + if (olen >= LONG_MAX - len) { rb_raise(rb_eArgError, "string sizes too big"); } - total = RSTRING_LEN(str)+len; + total = olen + len; if (capa <= total) { while (total > capa) { if (capa > LONG_MAX / 2) { @@ -2191,14 +2202,15 @@ str_buf_cat(VALUE str, const char *ptr, long len) } capa = 2 * capa; } - RESIZE_CAPA(str, capa); + RESIZE_CAPA_TERM(str, capa, termlen); + sptr = RSTRING_PTR(str); } if (off != -1) { - ptr = RSTRING_PTR(str) + off; + ptr = sptr + off; } - memcpy(RSTRING_PTR(str) + RSTRING_LEN(str), ptr, len); + memcpy(sptr + olen, ptr, len); STR_SET_LEN(str, total); - RSTRING_PTR(str)[total] = '\0'; /* sentinel */ + TERM_FILL(sptr + total, termlen); /* sentinel */ return str; } diff --git a/test/-ext-/string/test_cstr.rb b/test/-ext-/string/test_cstr.rb index 6f2fdef3d4..272e090955 100644 --- a/test/-ext-/string/test_cstr.rb +++ b/test/-ext-/string/test_cstr.rb @@ -86,6 +86,26 @@ class Test_StringCStr < Test::Unit::TestCase } end + def test_embedded_from_heap + gh821 = "[GH-821]" + embedded_string = "abcdefghi" + string = embedded_string.gsub("efg", "123") + {}[string] = 1 + non_terminated = "#{string}#{nil}" + assert_nil(Bug::String.cstr_term_char(non_terminated), gh821) + + result = {} + WCHARS.map do |enc| + embedded_string = "ab".encode(enc) + string = embedded_string.gsub("b".encode(enc), "1".encode(enc)) + {}[string] = 1 + non_terminated = "#{string}#{nil}" + c = Bug::String.cstr_term_char(non_terminated) + result[enc] = c if c + end + assert_empty(result, gh821) + end + def assert_wchars_term_char(str) result = {} WCHARS.map do |enc| diff --git a/version.h b/version.h index 00419b19c4..088f1a924c 100644 --- a/version.h +++ b/version.h @@ -1,6 +1,6 @@ #define RUBY_VERSION "2.2.0" #define RUBY_RELEASE_DATE "2015-02-19" -#define RUBY_PATCHLEVEL 54 +#define RUBY_PATCHLEVEL 55 #define RUBY_RELEASE_YEAR 2015 #define RUBY_RELEASE_MONTH 2