diff --git a/backport-0001-CVE-2021-41817.patch b/backport-0001-CVE-2021-41817.patch new file mode 100644 index 0000000000000000000000000000000000000000..fbede1b4eac64e643b352e40eb71f3f2d00629df --- /dev/null +++ b/backport-0001-CVE-2021-41817.patch @@ -0,0 +1,911 @@ +From 489c8cebf575741d62effd0d212f1319beff3c40 Mon Sep 17 00:00:00 2001 +From: Yusuke Endoh +Date: Fri, 12 Nov 2021 12:15:25 +0900 +Subject: [PATCH] [ruby/date] Add length limit option for methods that parses + date strings + + `Date.parse` now raises an ArgumentError when a given date string is +longer than 128. You can configure the limit by giving `limit` keyword +arguments like `Date.parse(str, limit:1000)`. If you pass `limit:nil`, +the limit is disabled. + +Not only `Date.parse` but also the following methods are changed. + +* Date._parse +* Date.parse +* DateTime.parse +* Date._iso8601 +* Date.iso8601 +* DateTime.iso8601 +* Date._rfc3339 +* Date.rfc3339 +* DateTime.rfc3339 +* Date._xmlschema +* Date.xmlschema +* DateTime.xmlschema +* Date._rfc2822 +* Date.rfc2822 +* DateTime.rfc2822 +* Date._rfc822 +* Date.rfc822 +* DateTime.rfc822 +* Date._jisx0301 +* Date.jisx0301 +* DateTime.jisx0301 + +https://github.com/ruby/date/commit/3959accef8 +--- + ext/date/date_core.c | 384 +++++++++++++++++++++++++++-------- + test/date/test_date_parse.rb | 29 +++ + 2 files changed, 325 insertions(+), 88 deletions(-) + +diff --git a/ext/date/date_core.c b/ext/date/date_core.c +index c250633..177ea0f 100644 +--- a/ext/date/date_core.c ++++ b/ext/date/date_core.c +@@ -4290,12 +4290,37 @@ date_s_strptime(int argc, VALUE *argv, VALUE klass) + + VALUE date__parse(VALUE str, VALUE comp); + ++static size_t ++get_limit(VALUE opt) ++{ ++ if (!NIL_P(opt)) { ++ VALUE limit = rb_hash_aref(opt, ID2SYM(rb_intern("limit"))); ++ if (NIL_P(limit)) return SIZE_MAX; ++ return NUM2SIZET(limit); ++ } ++ return 128; ++} ++ ++static void ++check_limit(VALUE str, VALUE opt) ++{ ++ StringValue(str); ++ size_t slen = RSTRING_LEN(str); ++ size_t limit = get_limit(opt); ++ if (slen > limit) { ++ rb_raise(rb_eArgError, ++ "string length (%"PRI_SIZE_PREFIX"u) exceeds the limit %"PRI_SIZE_PREFIX"u", slen, limit); ++ } ++} ++ + static VALUE + date_s__parse_internal(int argc, VALUE *argv, VALUE klass) + { +- VALUE vstr, vcomp, hash; ++ VALUE vstr, vcomp, hash, opt; + +- rb_scan_args(argc, argv, "11", &vstr, &vcomp); ++ rb_scan_args(argc, argv, "11:", &vstr, &vcomp, &opt); ++ if (!NIL_P(opt)) argc--; ++ check_limit(vstr, opt); + StringValue(vstr); + if (!rb_enc_str_asciicompat_p(vstr)) + rb_raise(rb_eArgError, +@@ -4320,7 +4345,7 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass) + + /* + * call-seq: +- * Date._parse(string[, comp=true]) -> hash ++ * Date._parse(string[, comp=true], limit: 128) -> hash + * + * Parses the given representation of date and time, and returns a + * hash of parsed elements. This method does not function as a +@@ -4331,6 +4356,10 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass) + * it full. + * + * Date._parse('2001-02-03') #=> {:year=>2001, :mon=>2, :mday=>3} ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + date_s__parse(int argc, VALUE *argv, VALUE klass) +@@ -4340,7 +4369,7 @@ date_s__parse(int argc, VALUE *argv, VALUE klass) + + /* + * call-seq: +- * Date.parse(string='-4712-01-01'[, comp=true[, start=Date::ITALY]]) -> date ++ * Date.parse(string='-4712-01-01'[, comp=true[, start=Date::ITALY]], limit: 128) -> date + * + * Parses the given representation of date and time, and creates a + * date object. This method does not function as a validator. +@@ -4352,13 +4381,18 @@ date_s__parse(int argc, VALUE *argv, VALUE klass) + * Date.parse('2001-02-03') #=> # + * Date.parse('20010203') #=> # + * Date.parse('3rd Feb 2001') #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + date_s_parse(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, comp, sg; ++ VALUE str, comp, sg, opt; + +- rb_scan_args(argc, argv, "03", &str, &comp, &sg); ++ rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -4370,11 +4404,12 @@ date_s_parse(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE argv2[2], hash; +- +- argv2[0] = str; +- argv2[1] = comp; +- hash = date_s__parse(2, argv2, klass); ++ int argc2 = 2; ++ VALUE argv2[3]; ++ argv2[0] = str; ++ argv2[1] = comp; ++ if (!NIL_P(opt)) argv2[argc2++] = opt; ++ VALUE hash = date_s__parse(argc2, argv2, klass); + return d_new_by_frags(klass, hash, sg); + } + } +@@ -4388,19 +4423,28 @@ VALUE date__jisx0301(VALUE); + + /* + * call-seq: +- * Date._iso8601(string) -> hash ++ * Date._iso8601(string, limit: 128) -> hash + * + * Returns a hash of parsed elements. ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE +-date_s__iso8601(VALUE klass, VALUE str) ++date_s__iso8601(int argc, VALUE *argv, VALUE klass) + { ++ VALUE str, opt; ++ ++ rb_scan_args(argc, argv, "1:", &str, &opt); ++ check_limit(str, opt); ++ + return date__iso8601(str); + } + + /* + * call-seq: +- * Date.iso8601(string='-4712-01-01'[, start=Date::ITALY]) -> date ++ * Date.iso8601(string='-4712-01-01'[, start=Date::ITALY], limit: 128) -> date + * + * Creates a new Date object by parsing from a string according to + * some typical ISO 8601 formats. +@@ -4408,13 +4452,18 @@ date_s__iso8601(VALUE klass, VALUE str) + * Date.iso8601('2001-02-03') #=> # + * Date.iso8601('20010203') #=> # + * Date.iso8601('2001-W05-6') #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + date_s_iso8601(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -4424,38 +4473,56 @@ date_s_iso8601(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__iso8601(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ if (!NIL_P(opt)) argv2[argc2++] = opt; ++ VALUE hash = date_s__iso8601(argc2, argv2, klass); + return d_new_by_frags(klass, hash, sg); + } + } + + /* + * call-seq: +- * Date._rfc3339(string) -> hash ++ * Date._rfc3339(string, limit: 128) -> hash + * + * Returns a hash of parsed elements. ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE +-date_s__rfc3339(VALUE klass, VALUE str) ++date_s__rfc3339(int argc, VALUE *argv, VALUE klass) + { ++ VALUE str, opt; ++ ++ rb_scan_args(argc, argv, "1:", &str, &opt); ++ check_limit(str, opt); ++ + return date__rfc3339(str); + } + + /* + * call-seq: +- * Date.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> date ++ * Date.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> date + * + * Creates a new Date object by parsing from a string according to + * some typical RFC 3339 formats. + * + * Date.rfc3339('2001-02-03T04:05:06+07:00') #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + date_s_rfc3339(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -4465,38 +4532,56 @@ date_s_rfc3339(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__rfc3339(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ if (!NIL_P(opt)) argv2[argc2++] = opt; ++ VALUE hash = date_s__rfc3339(argc2, argv2, klass); + return d_new_by_frags(klass, hash, sg); + } + } + + /* + * call-seq: +- * Date._xmlschema(string) -> hash ++ * Date._xmlschema(string, limit: 128) -> hash + * + * Returns a hash of parsed elements. ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE +-date_s__xmlschema(VALUE klass, VALUE str) ++date_s__xmlschema(int argc, VALUE *argv, VALUE klass) + { ++ VALUE str, opt; ++ ++ rb_scan_args(argc, argv, "1:", &str, &opt); ++ check_limit(str, opt); ++ + return date__xmlschema(str); + } + + /* + * call-seq: +- * Date.xmlschema(string='-4712-01-01'[, start=Date::ITALY]) -> date ++ * Date.xmlschema(string='-4712-01-01'[, start=Date::ITALY], limit: 128) -> date + * + * Creates a new Date object by parsing from a string according to + * some typical XML Schema formats. + * + * Date.xmlschema('2001-02-03') #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + date_s_xmlschema(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -4506,41 +4591,58 @@ date_s_xmlschema(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__xmlschema(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ if (!NIL_P(opt)) argv2[argc2++] = opt; ++ VALUE hash = date_s__xmlschema(argc2, argv2, klass); + return d_new_by_frags(klass, hash, sg); + } + } + + /* + * call-seq: +- * Date._rfc2822(string) -> hash +- * Date._rfc822(string) -> hash ++ * Date._rfc2822(string, limit: 128) -> hash ++ * Date._rfc822(string, limit: 128) -> hash + * + * Returns a hash of parsed elements. ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE +-date_s__rfc2822(VALUE klass, VALUE str) ++date_s__rfc2822(int argc, VALUE *argv, VALUE klass) + { ++ VALUE str, opt; ++ ++ rb_scan_args(argc, argv, "1:", &str, &opt); ++ check_limit(str, opt); ++ + return date__rfc2822(str); + } + + /* + * call-seq: +- * Date.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> date +- * Date.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> date ++ * Date.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> date ++ * Date.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> date + * + * Creates a new Date object by parsing from a string according to + * some typical RFC 2822 formats. + * + * Date.rfc2822('Sat, 3 Feb 2001 00:00:00 +0000') + * #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + date_s_rfc2822(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); + + switch (argc) { + case 0: +@@ -4550,39 +4652,56 @@ date_s_rfc2822(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__rfc2822(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ if (!NIL_P(opt)) argv2[argc2++] = opt; ++ VALUE hash = date_s__rfc2822(argc2, argv2, klass); + return d_new_by_frags(klass, hash, sg); + } + } + + /* + * call-seq: +- * Date._httpdate(string) -> hash ++ * Date._httpdate(string, limit: 128) -> hash + * + * Returns a hash of parsed elements. ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE +-date_s__httpdate(VALUE klass, VALUE str) ++date_s__httpdate(int argc, VALUE *argv, VALUE klass) + { ++ VALUE str, opt; ++ ++ rb_scan_args(argc, argv, "1:", &str, &opt); ++ check_limit(str, opt); ++ + return date__httpdate(str); + } + + /* + * call-seq: +- * Date.httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=Date::ITALY]) -> date ++ * Date.httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=Date::ITALY], limit: 128) -> date + * + * Creates a new Date object by parsing from a string according to + * some RFC 2616 format. + * + * Date.httpdate('Sat, 03 Feb 2001 00:00:00 GMT') + * #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + date_s_httpdate(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); + + switch (argc) { + case 0: +@@ -4592,38 +4711,56 @@ date_s_httpdate(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__httpdate(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ if (!NIL_P(opt)) argv2[argc2++] = opt; ++ VALUE hash = date_s__httpdate(argc2, argv2, klass); + return d_new_by_frags(klass, hash, sg); + } + } + + /* + * call-seq: +- * Date._jisx0301(string) -> hash ++ * Date._jisx0301(string, limit: 128) -> hash + * + * Returns a hash of parsed elements. ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE +-date_s__jisx0301(VALUE klass, VALUE str) ++date_s__jisx0301(int argc, VALUE *argv, VALUE klass) + { ++ VALUE str, opt; ++ ++ rb_scan_args(argc, argv, "1:", &str, &opt); ++ check_limit(str, opt); ++ + return date__jisx0301(str); + } + + /* + * call-seq: +- * Date.jisx0301(string='-4712-01-01'[, start=Date::ITALY]) -> date ++ * Date.jisx0301(string='-4712-01-01'[, start=Date::ITALY], limit: 128) -> date + * + * Creates a new Date object by parsing from a string according to + * some typical JIS X 0301 formats. + * + * Date.jisx0301('H13.02.03') #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + date_s_jisx0301(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -4633,7 +4770,11 @@ date_s_jisx0301(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__jisx0301(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ if (!NIL_P(opt)) argv2[argc2++] = opt; ++ VALUE hash = date_s__jisx0301(argc2, argv2, klass); + return d_new_by_frags(klass, hash, sg); + } + } +@@ -7925,7 +8066,7 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass) + + /* + * call-seq: +- * DateTime.parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=Date::ITALY]]) -> datetime ++ * DateTime.parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=Date::ITALY]], limit: 128) -> datetime + * + * Parses the given representation of date and time, and creates a + * DateTime object. This method does not function as a validator. +@@ -7939,13 +8080,18 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass) + * #=> # + * DateTime.parse('3rd Feb 2001 04:05:06 PM') + * #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + datetime_s_parse(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, comp, sg; ++ VALUE str, comp, sg, opt; + +- rb_scan_args(argc, argv, "03", &str, &comp, &sg); ++ rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -7957,18 +8103,20 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE argv2[2], hash; +- +- argv2[0] = str; +- argv2[1] = comp; +- hash = date_s__parse(2, argv2, klass); ++ int argc2 = 2; ++ VALUE argv2[3]; ++ argv2[0] = str; ++ argv2[1] = comp; ++ argv2[2] = opt; ++ if (!NIL_P(opt)) argc2++; ++ VALUE hash = date_s__parse(argc2, argv2, klass); + return dt_new_by_frags(klass, hash, sg); + } + } + + /* + * call-seq: +- * DateTime.iso8601(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime ++ * DateTime.iso8601(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime + * + * Creates a new DateTime object by parsing from a string according to + * some typical ISO 8601 formats. +@@ -7979,13 +8127,18 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass) + * #=> # + * DateTime.iso8601('2001-W05-6T04:05:06+07:00') + * #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + datetime_s_iso8601(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -7995,27 +8148,37 @@ datetime_s_iso8601(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__iso8601(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ argv2[1] = opt; ++ if (!NIL_P(opt)) argc2--; ++ VALUE hash = date_s__iso8601(argc2, argv2, klass); + return dt_new_by_frags(klass, hash, sg); + } + } + + /* + * call-seq: +- * DateTime.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime ++ * DateTime.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime + * + * Creates a new DateTime object by parsing from a string according to + * some typical RFC 3339 formats. + * + * DateTime.rfc3339('2001-02-03T04:05:06+07:00') + * #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -8025,27 +8188,37 @@ datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__rfc3339(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ argv2[1] = opt; ++ if (!NIL_P(opt)) argc2++; ++ VALUE hash = date_s__rfc3339(argc2, argv2, klass); + return dt_new_by_frags(klass, hash, sg); + } + } + + /* + * call-seq: +- * DateTime.xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime ++ * DateTime.xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime + * + * Creates a new DateTime object by parsing from a string according to + * some typical XML Schema formats. + * + * DateTime.xmlschema('2001-02-03T04:05:06+07:00') + * #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -8055,28 +8228,38 @@ datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__xmlschema(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ argv2[1] = opt; ++ if (!NIL_P(opt)) argc2++; ++ VALUE hash = date_s__xmlschema(argc2, argv2, klass); + return dt_new_by_frags(klass, hash, sg); + } + } + + /* + * call-seq: +- * DateTime.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> datetime +- * DateTime.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> datetime ++ * DateTime.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> datetime ++ * DateTime.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> datetime + * + * Creates a new DateTime object by parsing from a string according to + * some typical RFC 2822 formats. + * + * DateTime.rfc2822('Sat, 3 Feb 2001 04:05:06 +0700') + * #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -8086,7 +8269,12 @@ datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__rfc2822(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ argv2[1] = opt; ++ if (!NIL_P(opt)) argc2++; ++ VALUE hash = date_s__rfc2822(argc2, argv2, klass); + return dt_new_by_frags(klass, hash, sg); + } + } +@@ -8100,13 +8288,18 @@ datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass) + * + * DateTime.httpdate('Sat, 03 Feb 2001 04:05:06 GMT') + * #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + datetime_s_httpdate(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -8116,27 +8309,37 @@ datetime_s_httpdate(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__httpdate(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ argv2[1] = opt; ++ if (!NIL_P(opt)) argc2++; ++ VALUE hash = date_s__httpdate(argc2, argv2, klass); + return dt_new_by_frags(klass, hash, sg); + } + } + + /* + * call-seq: +- * DateTime.jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime ++ * DateTime.jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime + * + * Creates a new DateTime object by parsing from a string according to + * some typical JIS X 0301 formats. + * + * DateTime.jisx0301('H13.02.03T04:05:06+07:00') + * #=> # ++ * ++ * Raise an ArgumentError when the string length is longer than _limit_. ++ * You can stop this check by passing , but note that ++ * it may take a long time to parse. + */ + static VALUE + datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass) + { +- VALUE str, sg; ++ VALUE str, sg, opt; + +- rb_scan_args(argc, argv, "02", &str, &sg); ++ rb_scan_args(argc, argv, "02:", &str, &sg, &opt); ++ if (!NIL_P(opt)) argc--; + + switch (argc) { + case 0: +@@ -8146,7 +8349,12 @@ datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass) + } + + { +- VALUE hash = date_s__jisx0301(klass, str); ++ int argc2 = 1; ++ VALUE argv2[2]; ++ argv2[0] = str; ++ argv2[1] = opt; ++ if (!NIL_P(opt)) argc2++; ++ VALUE hash = date_s__jisx0301(argc2, argv2, klass); + return dt_new_by_frags(klass, hash, sg); + } + } +@@ -9297,19 +9505,19 @@ Init_date_core(void) + rb_define_singleton_method(cDate, "strptime", date_s_strptime, -1); + rb_define_singleton_method(cDate, "_parse", date_s__parse, -1); + rb_define_singleton_method(cDate, "parse", date_s_parse, -1); +- rb_define_singleton_method(cDate, "_iso8601", date_s__iso8601, 1); ++ rb_define_singleton_method(cDate, "_iso8601", date_s__iso8601, -1); + rb_define_singleton_method(cDate, "iso8601", date_s_iso8601, -1); +- rb_define_singleton_method(cDate, "_rfc3339", date_s__rfc3339, 1); ++ rb_define_singleton_method(cDate, "_rfc3339", date_s__rfc3339, -1); + rb_define_singleton_method(cDate, "rfc3339", date_s_rfc3339, -1); +- rb_define_singleton_method(cDate, "_xmlschema", date_s__xmlschema, 1); ++ rb_define_singleton_method(cDate, "_xmlschema", date_s__xmlschema, -1); + rb_define_singleton_method(cDate, "xmlschema", date_s_xmlschema, -1); +- rb_define_singleton_method(cDate, "_rfc2822", date_s__rfc2822, 1); +- rb_define_singleton_method(cDate, "_rfc822", date_s__rfc2822, 1); ++ rb_define_singleton_method(cDate, "_rfc2822", date_s__rfc2822, -1); ++ rb_define_singleton_method(cDate, "_rfc822", date_s__rfc2822, -1); + rb_define_singleton_method(cDate, "rfc2822", date_s_rfc2822, -1); + rb_define_singleton_method(cDate, "rfc822", date_s_rfc2822, -1); +- rb_define_singleton_method(cDate, "_httpdate", date_s__httpdate, 1); ++ rb_define_singleton_method(cDate, "_httpdate", date_s__httpdate, -1); + rb_define_singleton_method(cDate, "httpdate", date_s_httpdate, -1); +- rb_define_singleton_method(cDate, "_jisx0301", date_s__jisx0301, 1); ++ rb_define_singleton_method(cDate, "_jisx0301", date_s__jisx0301, -1); + rb_define_singleton_method(cDate, "jisx0301", date_s_jisx0301, -1); + + #ifndef NDEBUG +diff --git a/test/date/test_date_parse.rb b/test/date/test_date_parse.rb +index ac0eb85..f9b160e 100644 +--- a/test/date/test_date_parse.rb ++++ b/test/date/test_date_parse.rb +@@ -1,6 +1,7 @@ + # frozen_string_literal: true + require 'test/unit' + require 'date' ++require 'timeout' + + class TestDateParse < Test::Unit::TestCase + +@@ -1122,4 +1123,32 @@ class TestDateParse < Test::Unit::TestCase + assert_equal(s0, s) + end + ++ def test_length_limit ++ assert_raise(ArgumentError) { Date._parse("1" * 1000) } ++ assert_raise(ArgumentError) { Date._iso8601("1" * 1000) } ++ assert_raise(ArgumentError) { Date._rfc3339("1" * 1000) } ++ assert_raise(ArgumentError) { Date._xmlschema("1" * 1000) } ++ assert_raise(ArgumentError) { Date._rfc2822("1" * 1000) } ++ assert_raise(ArgumentError) { Date._rfc822("1" * 1000) } ++ assert_raise(ArgumentError) { Date._jisx0301("1" * 1000) } ++ ++ assert_raise(ArgumentError) { Date.parse("1" * 1000) } ++ assert_raise(ArgumentError) { Date.iso8601("1" * 1000) } ++ assert_raise(ArgumentError) { Date.rfc3339("1" * 1000) } ++ assert_raise(ArgumentError) { Date.xmlschema("1" * 1000) } ++ assert_raise(ArgumentError) { Date.rfc2822("1" * 1000) } ++ assert_raise(ArgumentError) { Date.rfc822("1" * 1000) } ++ assert_raise(ArgumentError) { Date.jisx0301("1" * 1000) } ++ ++ assert_raise(ArgumentError) { DateTime.parse("1" * 1000) } ++ assert_raise(ArgumentError) { DateTime.iso8601("1" * 1000) } ++ assert_raise(ArgumentError) { DateTime.rfc3339("1" * 1000) } ++ assert_raise(ArgumentError) { DateTime.xmlschema("1" * 1000) } ++ assert_raise(ArgumentError) { DateTime.rfc2822("1" * 1000) } ++ assert_raise(ArgumentError) { DateTime.rfc822("1" * 1000) } ++ assert_raise(ArgumentError) { DateTime.jisx0301("1" * 1000) } ++ ++ assert_raise(ArgumentError) { Date._parse("Jan " + "9" * 1000000) } ++ assert_raise(Timeout::Error) { Timeout.timeout(1) { Date._parse("Jan " + "9" * 1000000, limit: nil) } } ++ end + end +-- +2.33.0 + + diff --git a/backport-0002-CVE-2021-41817.patch b/backport-0002-CVE-2021-41817.patch new file mode 100644 index 0000000000000000000000000000000000000000..fcf73dd4a129207239c95d9b459526235d7fac63 --- /dev/null +++ b/backport-0002-CVE-2021-41817.patch @@ -0,0 +1,101 @@ +From fa674cf7230e40bc96625ee97a6057e48bb20f0f Mon Sep 17 00:00:00 2001 +From: Jean Boussier +Date: Mon, 15 Nov 2021 11:37:40 +0100 +Subject: [PATCH] [ruby/date] `Date._(nil)` should return an empty Hash + +Fix: https://github.com/ruby/date/issues/39 + +This is how versions previous to 3.2.1 behaved and Active Support +currently rely on this behavior. + +https://github.com/rails/rails/blob/90357af08048ef5076730505f6e7b14a81f33d0c/activesupport/lib/active_support/values/time_zone.rb#L383-L384 + +Any Rails application upgrading to date `3.2.1` might run into unexpected errors. + +https://github.com/ruby/date/commit/8f2d7a0c7e +--- + ext/date/date_core.c | 2 ++ + test/date/test_date_parse.rb | 18 ++++++++++++++++++ + 2 files changed, 20 insertions(+) + +diff --git a/ext/date/date_core.c b/ext/date/date_core.c +index 177ea0f..516640b 100644 +--- a/ext/date/date_core.c ++++ b/ext/date/date_core.c +@@ -4304,6 +4304,8 @@ get_limit(VALUE opt) + static void + check_limit(VALUE str, VALUE opt) + { ++ if (NIL_P(str)) return; ++ + StringValue(str); + size_t slen = RSTRING_LEN(str); + size_t limit = get_limit(opt); +diff --git a/test/date/test_date_parse.rb b/test/date/test_date_parse.rb +index f9b160e..dfc018b 100644 +--- a/test/date/test_date_parse.rb ++++ b/test/date/test_date_parse.rb +@@ -824,6 +824,9 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._iso8601('') + assert_equal({}, h) ++ ++ h = Date._iso8601(nil) ++ assert_equal({}, h) + end + + def test__rfc3339 +@@ -839,6 +842,9 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._rfc3339('') + assert_equal({}, h) ++ ++ h = Date._rfc3339(nil) ++ assert_equal({}, h) + end + + def test__xmlschema +@@ -921,6 +927,9 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._xmlschema('') + assert_equal({}, h) ++ ++ h = Date._xmlschema(nil) ++ assert_equal({}, h) + end + + def test__rfc2822 +@@ -953,6 +962,9 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._rfc2822('') + assert_equal({}, h) ++ ++ h = Date._rfc2822(nil) ++ assert_equal({}, h) + end + + def test__httpdate +@@ -973,6 +985,9 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._httpdate('') + assert_equal({}, h) ++ ++ h = Date._httpdate(nil) ++ assert_equal({}, h) + end + + def test__jisx0301 +@@ -1001,6 +1016,9 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._jisx0301('') + assert_equal({}, h) ++ ++ h = Date._jisx0301(nil) ++ assert_equal({}, h) + end + + def test_iso8601 +-- +2.33.0 + + diff --git a/backport-0003-CVE-2021-41817.patch b/backport-0003-CVE-2021-41817.patch new file mode 100644 index 0000000000000000000000000000000000000000..26124e35afb741e08bb37b45488e23e468519da4 --- /dev/null +++ b/backport-0003-CVE-2021-41817.patch @@ -0,0 +1,97 @@ +From a87c56f820dac90d50544ad33cc546daa9f29a9a Mon Sep 17 00:00:00 2001 +From: Jean Boussier +Date: Tue, 16 Nov 2021 14:03:42 +0100 +Subject: [PATCH] [ruby/date] check_limit: also handle symbols + +https://github.com/ruby/date/commit/376c65942b +--- + ext/date/date_core.c | 1 + + test/date/test_date_parse.rb | 24 ++++++++++++++++++++++++ + 2 files changed, 25 insertions(+) + +diff --git a/ext/date/date_core.c b/ext/date/date_core.c +index 516640b..0a9468a 100644 +--- a/ext/date/date_core.c ++++ b/ext/date/date_core.c +@@ -4305,6 +4305,7 @@ static void + check_limit(VALUE str, VALUE opt) + { + if (NIL_P(str)) return; ++ if (SYMBOL_P(str)) str = rb_sym2str(str); + + StringValue(str); + size_t slen = RSTRING_LEN(str); +diff --git a/test/date/test_date_parse.rb b/test/date/test_date_parse.rb +index dfc018b..5bc2ceb 100644 +--- a/test/date/test_date_parse.rb ++++ b/test/date/test_date_parse.rb +@@ -827,6 +827,10 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._iso8601(nil) + assert_equal({}, h) ++ ++ h = Date._iso8601('01-02-03T04:05:06Z'.to_sym) ++ assert_equal([2001, 2, 3, 4, 5, 6, 0], ++ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset)) + end + + def test__rfc3339 +@@ -845,6 +849,10 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._rfc3339(nil) + assert_equal({}, h) ++ ++ h = Date._rfc3339('2001-02-03T04:05:06Z'.to_sym) ++ assert_equal([2001, 2, 3, 4, 5, 6, 0], ++ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset)) + end + + def test__xmlschema +@@ -930,6 +938,10 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._xmlschema(nil) + assert_equal({}, h) ++ ++ h = Date._xmlschema('2001-02-03'.to_sym) ++ assert_equal([2001, 2, 3, nil, nil, nil, nil], ++ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset)) + end + + def test__rfc2822 +@@ -965,6 +977,10 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._rfc2822(nil) + assert_equal({}, h) ++ ++ h = Date._rfc2822('Sat, 3 Feb 2001 04:05:06 UT'.to_sym) ++ assert_equal([2001, 2, 3, 4, 5, 6, 0], ++ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset)) + end + + def test__httpdate +@@ -988,6 +1004,10 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._httpdate(nil) + assert_equal({}, h) ++ ++ h = Date._httpdate('Sat, 03 Feb 2001 04:05:06 GMT'.to_sym) ++ assert_equal([2001, 2, 3, 4, 5, 6, 0], ++ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset)) + end + + def test__jisx0301 +@@ -1019,6 +1039,10 @@ class TestDateParse < Test::Unit::TestCase + + h = Date._jisx0301(nil) + assert_equal({}, h) ++ ++ h = Date._jisx0301('H13.02.03T04:05:06.07+0100'.to_sym) ++ assert_equal([2001, 2, 3, 4, 5, 6, 3600], ++ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset)) + end + + def test_iso8601 +-- +2.33.0 + + diff --git a/ruby.spec b/ruby.spec index d9c8a58814a83cc73f839193ded677eb3bf42705..e5d7163163ffe62afa773d9e4ab1389c9fa51ddf 100644 --- a/ruby.spec +++ b/ruby.spec @@ -1,6 +1,6 @@ Name: ruby Version: 2.5.8 -Release: 131 +Release: 132 Summary: Object-oriented scripting language interpreter License: (Ruby or BSD) and Public Domain and MIT and CC0 and zlib and UCD URL: https://www.ruby-lang.org/ @@ -74,6 +74,9 @@ Patch6030: backport-CVE-2024-39908-CVE-2024-41123-upgrade-lib-rexml-to-3.3.3.pat Patch6031: backport-CVE-2024-43398-upgrade-lib-rexml-to-3.3.6.patch Patch6032: backport-CVE-2024-47220.patch Patch6033: backport-CVE-2024-49761.patch +Patch6034: backport-0001-CVE-2021-41817.patch +Patch6035: backport-0002-CVE-2021-41817.patch +Patch6036: backport-0003-CVE-2021-41817.patch Patch9000: add-require_relative-helper-to-uninitialized-constan.patch @@ -613,6 +616,9 @@ make runruby TESTRUN_SCRIPT=%{SOURCE13} %exclude %{gem_dir}/gems/xmlrpc-0.3.0/.* %changelog +* Thu Dec 19 2024 shixuantong - 2.5.8-132 +- fix CVE-2021-41817 + * Tue Oct 29 2024 shixuantong - 2.5.8-131 - fix CVE-2024-49761