diff --git a/strftime.c b/strftime.c index 9bea3233f83..03e211f8f4f 100644 --- a/strftime.c +++ b/strftime.c @@ -173,11 +173,15 @@ resize_buffer(VALUE ftime, char *s, const char **start, const char **endp, size_t len = s - *start; size_t need = len + n * 2; size_t nlen = rb_str_capacity(ftime); - while (nlen < need) nlen <<= 1; - if (nlen < len || nlen > maxsize) { - return 0; + if (nlen > maxsize) return 0; + while (nlen < need) { + nlen <<= 1; + if (nlen < len || nlen > maxsize) { + return 0; + } } + rb_str_set_len(ftime, len); rb_str_modify_expand(ftime, nlen-len); s = RSTRING_PTR(ftime); @@ -548,34 +552,35 @@ rb_strftime_with_timespec(VALUE ftime, const char *format, size_t format_len, else { sign = +1; } - switch (colons) { +#define NEEDS_FOR_ZONE(fmt) do { \ + const int zone_size = rb_strlen_lit(fmt); \ + if (precision < zone_size) precision = zone_size; \ + NEEDS(precision); \ + precision -= zone_size - rb_strlen_lit("hh"); \ + } while (0) + + switch (colons) { case 0: /* %z -> +hhmm */ - precision = precision <= 5 ? 2 : precision-3; - NEEDS(precision + 3); + NEEDS_FOR_ZONE("+hhmm"); break; case 1: /* %:z -> +hh:mm */ - precision = precision <= 6 ? 2 : precision-4; - NEEDS(precision + 4); + NEEDS_FOR_ZONE("+hh:mm"); break; case 2: /* %::z -> +hh:mm:ss */ - precision = precision <= 9 ? 2 : precision-7; - NEEDS(precision + 7); + NEEDS_FOR_ZONE("+hh:mm:ss"); break; case 3: /* %:::z -> +hh[:mm[:ss]] */ if (off % 3600 == 0) { - precision = precision <= 3 ? 2 : precision-1; - NEEDS(precision + 3); + NEEDS_FOR_ZONE("+hh"); } else if (off % 60 == 0) { - precision = precision <= 6 ? 2 : precision-4; - NEEDS(precision + 4); + NEEDS_FOR_ZONE("+hh:mm"); } else { - precision = precision <= 9 ? 2 : precision-7; - NEEDS(precision + 9); + NEEDS_FOR_ZONE("+hh:mm:ss"); } break; diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb index e4bb82f2f9a..0779d8b88bf 100644 --- a/test/ruby/test_time.rb +++ b/test/ruby/test_time.rb @@ -3,6 +3,7 @@ require 'test/unit' require 'delegate' require 'timeout' require 'delegate' +require 'rbconfig/sizeof' class TestTime < Test::Unit::TestCase def setup @@ -1114,6 +1115,12 @@ class TestTime < Test::Unit::TestCase assert_equal(" -0:55:00", t.strftime("%_10::z"), bug6323) assert_equal("-000:55:00", t.strftime("%10::z"), bug6323) assert_equal("-000000:55", t.strftime("%10:::z")) + + t = Time.at(0, in: 0) + max = RbConfig::LIMITS["INT_MAX"] + all_assertions_foreach(nil, *%W[%#{max}z %#{max}:z %#{max}::z %#{max}:::z]) do |z| + assert_raise(Errno::ERANGE) {t.strftime(z)} + end end def test_strftime_invalid_modifier