From b35aff5813193a1f676bb1ff7b390797a892ad4e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 5 Dec 2025 12:33:03 +0900 Subject: [PATCH 1/7] [DOC] Centerize Variable, English, and Constant columns --- doc/language/globals.md | 106 ++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/doc/language/globals.md b/doc/language/globals.md index b9315f5ff975e4..221ad17e44e0c3 100644 --- a/doc/language/globals.md +++ b/doc/language/globals.md @@ -14,70 +14,70 @@ require 'English' ### Exceptions -| Variable | English | Contains | -|-------------|-------------------|----------------------------------------------------| -| `$!` | `$ERROR_INFO` | Exception object; set by Kernel#raise. | -| `$@` | `$ERROR_POSITION` | Array of backtrace positions; set by Kernel#raise. | +| Variable | English | Contains | +|:--------:|:-----------------:|----------------------------------------------------| +| `$!` | `$ERROR_INFO` | Exception object; set by Kernel#raise. | +| `$@` | `$ERROR_POSITION` | Array of backtrace positions; set by Kernel#raise. | ### Pattern Matching -| Variable | English | Contains | -|---------------|---------------------|--------------------------------------------------| -| `$~` | `$LAST_MATCH_INFO` | MatchData object; set by matcher method. | -| `$&` | `$MATCH` | Matched substring; set by matcher method. | -| `` $` `` | `$PRE_MATCH` | Substring left of match; set by matcher method. | -| `$'` | `$POST_MATCH` | Substring right of match; set by matcher method. | -| `$+` | `$LAST_PAREN_MATCH` | Last group matched; set by matcher method. | -| `$1` | | First group matched; set by matcher method. | -| `$2` | | Second group matched; set by matcher method. | +| Variable | English | Contains | +|:-------------:|:-------------------:|--------------------------------------------------| +| `$~` | `$LAST_MATCH_INFO` | MatchData object; set by matcher method. | +| `$&` | `$MATCH` | Matched substring; set by matcher method. | +| `` $` `` | `$PRE_MATCH` | Substring left of match; set by matcher method. | +| `$'` | `$POST_MATCH` | Substring right of match; set by matcher method. | +| `$+` | `$LAST_PAREN_MATCH` | Last group matched; set by matcher method. | +| `$1` | | First group matched; set by matcher method. | +| `$2` | | Second group matched; set by matcher method. | | $_n_ | | nth group matched; set by matcher method. | ### Separators -| Variable | English | Contains | -|----------|----------------------------|--------------------------------------------| -| `$/` | `$INPUT_RECORD_SEPARATOR` | Input record separator; initially newline. | -| `$\` | `$OUTPUT_RECORD_SEPARATOR` | Output record separator; initially `nil`. | +| Variable | English | Contains | +|:--------:|:--------------------------:|--------------------------------------------| +| `$/` | `$INPUT_RECORD_SEPARATOR` | Input record separator; initially newline. | +| `$\` | `$OUTPUT_RECORD_SEPARATOR` | Output record separator; initially `nil`. | ### Streams -| Variable | English | Contains | -|-----------|-----------------------------|-----------------------------------------------| +| Variable | English | Contains | +|:---------:|:---------------------------:|-----------------------------------------------| | `$stdin` | | Standard input stream; initially `STDIN`. | | `$stdout` | | Standard input stream; initially `STDIOUT`. | | `$stderr` | | Standard input stream; initially `STDERR`. | -| `$<` | `$DEFAULT_INPUT` | Default standard input; `ARGF` or `$stdin`. | -| `$>` | `$DEFAULT_OUTPUT` | Default standard output; initially `$stdout`. | -| `$.` | `$INPUT_LINE_NUMBER`, `$NR` | Input position of most recently read stream. | -| `$_` | `$LAST_READ_LINE` | String from most recently read stream. | +| `$<` | `$DEFAULT_INPUT` | Default standard input; `ARGF` or `$stdin`. | +| `$>` | `$DEFAULT_OUTPUT` | Default standard output; initially `$stdout`. | +| `$.` | `$INPUT_LINE_NUMBER`, `$NR` | Input position of most recently read stream. | +| `$_` | `$LAST_READ_LINE` | String from most recently read stream. | ### Processes -| Variable | English | Contains | -|---------------------------|-----------------------|--------------------------------------------------------| -| `$0` | | Initially, the name of the executing program. | -| `$*` | `$ARGV` | Points to the `ARGV` array. | -| `$$` | `$PROCESS_ID`, `$PID` | Process ID of the current process. | -| `$?` | `$CHILD_STATUS` | Process::Status of most recently exited child process. | +| Variable | English | Contains | +|:-------------------------:|:---------------------:|--------------------------------------------------------| +| `$0` | | Initially, the name of the executing program. | +| `$*` | `$ARGV` | Points to the `ARGV` array. | +| `$$` | `$PROCESS_ID`, `$PID` | Process ID of the current process. | +| `$?` | `$CHILD_STATUS` | Process::Status of most recently exited child process. | | `$LOAD_PATH`, `$:`, `$-I` | | Array of paths to be searched. | | `$LOADED_FEATURES`, `$"` | | Array of paths to loaded files. | ### Debugging -| Variable | English | Contains | -|-------------|---------|--------------------------------------------------------| +| Variable | English | Contains | +|:-----------:|:-------:|--------------------------------------------------------| | `$FILENAME` | | The value returned by method ARGF.filename. | -| `$DEBUG` | | Initially, whether option `-d` or `--debug` was given. | +| `$DEBUG` | | Initially, whether option `-d` or `--debug` was given. | | `$VERBOSE` | | Initially, whether option `-V` or `-W` was given. | ### Other Variables | Variable | English | Contains | -|----------|---------|------------------------------------------------| -| `$-a` | | Whether option `-a` was given. | -| `$-i` | | Extension given with command-line option `-i`. | -| `$-l` | | Whether option `-l` was given. | -| `$-p` | | Whether option `-p` was given. | +|:--------:|:-------:|------------------------------------------------| +| `$-a` | | Whether option `-a` was given. | +| `$-i` | | Extension given with command-line option `-i`. | +| `$-l` | | Whether option `-l` was given. | +| `$-p` | | Whether option `-p` was given. | ## Exceptions @@ -374,33 +374,33 @@ Whether command-line option `-p` was given; read-only. ### Streams | Constant | Contains | -|----------|-------------------------| +|:--------:|-------------------------| | `STDIN` | Standard input stream. | | `STDOUT` | Standard output stream. | | `STDERR` | Standard error stream. | ### Environment -| Constant | Contains | -|-----------------------|-------------------------------------------------------------------------------| -| `ENV` | Hash of current environment variable names and values. | -| `ARGF` | String concatenation of files given on the command line, or `$stdin` if none. | -| `ARGV` | Array of the given command-line arguments. | -| `TOPLEVEL_BINDING` | Binding of the top level scope. | -| `RUBY_VERSION` | String Ruby version. | -| `RUBY_RELEASE_DATE` | String Ruby release date. | -| `RUBY_PLATFORM` | String Ruby platform. | -| `RUBY_PATCH_LEVEL` | String Ruby patch level. | -| `RUBY_REVISION` | String Ruby revision. | -| `RUBY_COPYRIGHT` | String Ruby copyright. | -| `RUBY_ENGINE` | String Ruby engine. | +| Constant | Contains | +|:---------------------:|-------------------------------------------------------------------------------| +| `ENV` | Hash of current environment variable names and values. | +| `ARGF` | String concatenation of files given on the command line, or `$stdin` if none. | +| `ARGV` | Array of the given command-line arguments. | +| `TOPLEVEL_BINDING` | Binding of the top level scope. | +| `RUBY_VERSION` | String Ruby version. | +| `RUBY_RELEASE_DATE` | String Ruby release date. | +| `RUBY_PLATFORM` | String Ruby platform. | +| `RUBY_PATCH_LEVEL` | String Ruby patch level. | +| `RUBY_REVISION` | String Ruby revision. | +| `RUBY_COPYRIGHT` | String Ruby copyright. | +| `RUBY_ENGINE` | String Ruby engine. | | `RUBY_ENGINE_VERSION` | String Ruby engine version. | -| `RUBY_DESCRIPTION` | String Ruby description. | +| `RUBY_DESCRIPTION` | String Ruby description. | ### Embedded Data | Constant | Contains | -|----------|--------------------------------------------------------------------| +|:--------:|--------------------------------------------------------------------| | `DATA` | File containing embedded data (lines following `__END__`, if any). | ## Streams From 1e7373ef3061a3e0f3010ee580e5d4f80baf2e1a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 5 Dec 2025 13:02:22 +0900 Subject: [PATCH 2/7] [DOC] Describe the global variables set by command line options These variables are set by command line options, but it is deprecated to assign them any value other than nil in ruby code. --- doc/language/globals.md | 43 ++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/doc/language/globals.md b/doc/language/globals.md index 221ad17e44e0c3..7030cc1bfd7b0c 100644 --- a/doc/language/globals.md +++ b/doc/language/globals.md @@ -34,10 +34,10 @@ require 'English' ### Separators -| Variable | English | Contains | -|:--------:|:--------------------------:|--------------------------------------------| -| `$/` | `$INPUT_RECORD_SEPARATOR` | Input record separator; initially newline. | -| `$\` | `$OUTPUT_RECORD_SEPARATOR` | Output record separator; initially `nil`. | +| Variable | English | Contains | +|:-----------:|:--------------------------:|--------------------------------------------| +| `$/`, `$-0` | `$INPUT_RECORD_SEPARATOR` | Input record separator; initially newline. | +| `$\` | `$OUTPUT_RECORD_SEPARATOR` | Output record separator; initially `nil`. | ### Streams @@ -72,12 +72,13 @@ require 'English' ### Other Variables -| Variable | English | Contains | -|:--------:|:-------:|------------------------------------------------| -| `$-a` | | Whether option `-a` was given. | -| `$-i` | | Extension given with command-line option `-i`. | -| `$-l` | | Whether option `-l` was given. | -| `$-p` | | Whether option `-p` was given. | +| Variable | English | Contains | +|:-----------:|:-------:|------------------------------------------------| +| `$-F`, `$;` | | Separator given with command-line option `-F`. | +| `$-a` | | Whether option `-a` was given. | +| `$-i` | | Extension given with command-line option `-i`. | +| `$-l` | | Whether option `-l` was given. | +| `$-p` | | Whether option `-p` was given. | ## Exceptions @@ -174,6 +175,10 @@ No \English. ### `$/` (Input Record Separator) An input record separator, initially newline. +Set by the command-line option `-0`. + +Setting to non-nil value by other than the command-line option is +deprecated. English - `$INPUT_RECORD_SEPARATOR`, `$RS`. @@ -182,6 +187,10 @@ Aliased as `$-0`. ### `$\` (Output Record Separator) An output record separator, initially `nil`. +Copied from `$/` when the command-line option `-l` is given. + +Setting to non-nil value by other than the command-line option is +deprecated. English - `$OUTPUT_RECORD_SEPARATOR`, `$ORS`. @@ -340,6 +349,16 @@ Aliased as `$-v` and `$-w`. ## Other Variables +### `$-F` + +The default field separator in String#split; must be a String or a +Regexp, and can be set with command-line option `-F`. + +Setting to non-nil value by other than the command-line option is +deprecated. + +Aliased as `$;`. + ### `$-a` Whether command-line option `-a` was given; read-only. @@ -365,8 +384,6 @@ Whether command-line option `-p` was given; read-only. ### `$,` -### `$;` - # Pre-Defined Global Constants ## Summary @@ -401,7 +418,7 @@ Whether command-line option `-p` was given; read-only. | Constant | Contains | |:--------:|--------------------------------------------------------------------| -| `DATA` | File containing embedded data (lines following `__END__`, if any). | +| `DATA` | File containing embedded data (lines following `__END__`, if any). | ## Streams From 1cad20e2d5179b283cbb1aabe2496449f8edcbcb Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 5 Dec 2025 16:12:49 +0900 Subject: [PATCH 3/7] [DOC] Describe `$F` This variation is used when `-a` option is given. --- doc/language/globals.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/language/globals.md b/doc/language/globals.md index 7030cc1bfd7b0c..b22363f1da1b71 100644 --- a/doc/language/globals.md +++ b/doc/language/globals.md @@ -79,6 +79,7 @@ require 'English' | `$-i` | | Extension given with command-line option `-i`. | | `$-l` | | Whether option `-l` was given. | | `$-p` | | Whether option `-p` was given. | +| `$F` | | Array of `$_` split by `$-F`. | ## Exceptions @@ -378,6 +379,12 @@ Whether command-line option `-l` was set; read-only. Whether command-line option `-p` was given; read-only. +### `$F` + +If the command line option `-a` is given, the array obtained by +splitting `$_` by `$-F` is assigned at the start of each `-l`/`-p` +loop. + ## Deprecated ### `$=` From 95ea3bd5ee9abc9e32c68e6a4fe2d795d853d907 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 5 Dec 2025 12:32:35 +0100 Subject: [PATCH 4/7] [ruby/timeout] Only the timeout method should be public on the Timeout module https://github.com/ruby/timeout/commit/cd51eac3ca --- lib/timeout.rb | 10 +++++++--- test/test_timeout.rb | 6 ++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index e1f0a4a78c6f59..f173d028a39d0a 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -133,6 +133,7 @@ def self.ensure_timeout_thread_created end end end + private_class_method :ensure_timeout_thread_created # We keep a private reference so that time mocking libraries won't break # Timeout. @@ -167,7 +168,7 @@ def self.ensure_timeout_thread_created # Note that this is both a method of module Timeout, so you can include # Timeout into your classes so they have a #timeout method, as well as # a module method, so you can call it directly as Timeout.timeout(). - def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ + def self.timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ return yield(sec) if sec == nil or sec.zero? raise ArgumentError, "Timeout sec must be a non-negative number" if 0 > sec @@ -177,7 +178,7 @@ def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ return scheduler.timeout_after(sec, klass || Error, message, &block) end - Timeout.ensure_timeout_thread_created + ensure_timeout_thread_created perform = Proc.new do |exc| request = Request.new(Thread.current, sec, exc, message) QUEUE_MUTEX.synchronize do @@ -197,5 +198,8 @@ def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ Error.handle_timeout(message, &perform) end end - module_function :timeout + + private def timeout(*args, &block) + Timeout.timeout(*args, &block) + end end diff --git a/test/test_timeout.rb b/test/test_timeout.rb index 01156867b05609..e367df757cc9b2 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -4,6 +4,12 @@ class TestTimeout < Test::Unit::TestCase + def test_public_methods + assert_equal [:timeout], Timeout.private_instance_methods(false) + assert_equal [], Timeout.public_instance_methods(false) + assert_equal [:timeout], Timeout.singleton_class.public_instance_methods(false) + end + def test_work_is_done_in_same_thread_as_caller assert_equal Thread.current, Timeout.timeout(10){ Thread.current } end From bf2b9c09473ee1a49766c6d7c6e1d683236a13ee Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 5 Dec 2025 02:52:38 +0900 Subject: [PATCH 5/7] [ruby/openssl] asn1: reorder declarations Move variable declarations for OpenSSL::ASN1 classes to the top of the file. asn1time_to_time() will need eASN1Error in the next patch. https://github.com/ruby/openssl/commit/6c0ef87897 --- ext/openssl/ossl_asn1.c | 92 ++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index cb13ac6ecf876d..91acf5c6522740 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -9,9 +9,48 @@ */ #include "ossl.h" -static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, - int depth, int yield, long *num_read); -static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self); +/********/ +/* + * ASN1 module + */ +#define ossl_asn1_get_value(o) rb_attr_get((o),sivVALUE) +#define ossl_asn1_get_tag(o) rb_attr_get((o),sivTAG) +#define ossl_asn1_get_tagging(o) rb_attr_get((o),sivTAGGING) +#define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS) +#define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH) + +#define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v)) +#define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v)) +#define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v)) +#define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v)) +#define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v)) + +VALUE mASN1; +static VALUE eASN1Error; + +VALUE cASN1Data; +static VALUE cASN1Primitive; +static VALUE cASN1Constructive; + +static VALUE cASN1EndOfContent; +static VALUE cASN1Boolean; /* BOOLEAN */ +static VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */ +static VALUE cASN1BitString; /* BIT STRING */ +static VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */ +static VALUE cASN1NumericString, cASN1PrintableString; +static VALUE cASN1T61String, cASN1VideotexString; +static VALUE cASN1IA5String, cASN1GraphicString; +static VALUE cASN1ISO64String, cASN1GeneralString; +static VALUE cASN1UniversalString, cASN1BMPString; +static VALUE cASN1Null; /* NULL */ +static VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */ +static VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */ +static VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ + +static VALUE sym_IMPLICIT, sym_EXPLICIT; +static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE; +static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS; +static ID id_each; /* * DATE conversion @@ -190,49 +229,6 @@ ossl_asn1obj_to_string_long_name(const ASN1_OBJECT *obj) return ossl_asn1obj_to_string_oid(obj); } -/********/ -/* - * ASN1 module - */ -#define ossl_asn1_get_value(o) rb_attr_get((o),sivVALUE) -#define ossl_asn1_get_tag(o) rb_attr_get((o),sivTAG) -#define ossl_asn1_get_tagging(o) rb_attr_get((o),sivTAGGING) -#define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS) -#define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH) - -#define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v)) -#define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v)) -#define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v)) -#define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v)) -#define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v)) - -VALUE mASN1; -static VALUE eASN1Error; - -VALUE cASN1Data; -static VALUE cASN1Primitive; -static VALUE cASN1Constructive; - -static VALUE cASN1EndOfContent; -static VALUE cASN1Boolean; /* BOOLEAN */ -static VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */ -static VALUE cASN1BitString; /* BIT STRING */ -static VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */ -static VALUE cASN1NumericString, cASN1PrintableString; -static VALUE cASN1T61String, cASN1VideotexString; -static VALUE cASN1IA5String, cASN1GraphicString; -static VALUE cASN1ISO64String, cASN1GeneralString; -static VALUE cASN1UniversalString, cASN1BMPString; -static VALUE cASN1Null; /* NULL */ -static VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */ -static VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */ -static VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ - -static VALUE sym_IMPLICIT, sym_EXPLICIT; -static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE; -static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS; -static ID id_each; - /* * Ruby to ASN1 converters */ @@ -777,6 +773,10 @@ ossl_asn1data_to_der(VALUE self) } } +static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self); +static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, + int depth, int yield, long *num_read); + static VALUE int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, VALUE tc, long *num_read) From f179885d3c454c6a98c23b2a977480657bb0f676 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Fri, 28 Feb 2025 03:10:35 +0900 Subject: [PATCH 6/7] [ruby/openssl] asn1: use ASN1_TIME_to_tm() to decode UTCTime and GeneralizedTime The current logic relies on sscanf() and error checks are almost entirely missing. It also assumes that ASN1_STRING contents are NUL terminated, which is undocumented and not guaranteed for all valid ASN1_TIME objects. Switch to using ASN1_TIME_to_tm() added in OpenSSL 1.1.1. It is also supported by LibreSSL and AWS-LC. In the long term, we may want to replace ASN1_TIME_to_tm() with a hand-rolled decoder, since the function is intended for a specific use-case. It is too permissive for strict DER, yet still does not support all valid DER inputs and silently drops information such as fractional seconds. However, it handles everything that the current sscanf() code could handle. https://github.com/ruby/openssl/commit/73484f6794 --- ext/openssl/ossl_asn1.c | 72 ++++++++++++++------------------------- test/openssl/test_asn1.rb | 59 +++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 60 deletions(-) diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 91acf5c6522740..b21a79484ccc4a 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -55,57 +55,35 @@ static ID id_each; /* * DATE conversion */ +static VALUE +time_utc_new(VALUE args) +{ + return rb_funcallv(rb_cTime, rb_intern("utc"), 6, (VALUE *)args); +} + +static VALUE +time_utc_new_rescue(VALUE args, VALUE exc) +{ + rb_raise(eASN1Error, "invalid time"); +} + VALUE asn1time_to_time(const ASN1_TIME *time) { struct tm tm; - VALUE argv[6]; - int count; - - memset(&tm, 0, sizeof(struct tm)); - - switch (time->type) { - case V_ASN1_UTCTIME: - count = sscanf((const char *)time->data, "%2d%2d%2d%2d%2d%2dZ", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, - &tm.tm_sec); - - if (count == 5) { - tm.tm_sec = 0; - } else if (count != 6) { - ossl_raise(rb_eTypeError, "bad UTCTIME format: \"%s\"", - time->data); - } - if (tm.tm_year < 50) { - tm.tm_year += 2000; - } else { - tm.tm_year += 1900; - } - break; - case V_ASN1_GENERALIZEDTIME: - count = sscanf((const char *)time->data, "%4d%2d%2d%2d%2d%2dZ", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, - &tm.tm_sec); - if (count == 5) { - tm.tm_sec = 0; - } - else if (count != 6) { - ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format: \"%s\"", - time->data); - } - break; - default: - rb_warning("unknown time format"); - return Qnil; - } - argv[0] = INT2NUM(tm.tm_year); - argv[1] = INT2NUM(tm.tm_mon); - argv[2] = INT2NUM(tm.tm_mday); - argv[3] = INT2NUM(tm.tm_hour); - argv[4] = INT2NUM(tm.tm_min); - argv[5] = INT2NUM(tm.tm_sec); - - return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv); + if (!ASN1_TIME_to_tm(time, &tm)) + ossl_raise(eASN1Error, "ASN1_TIME_to_tm"); + + VALUE args[] = { + INT2NUM(tm.tm_year + 1900), + INT2NUM(tm.tm_mon + 1), + INT2NUM(tm.tm_mday), + INT2NUM(tm.tm_hour), + INT2NUM(tm.tm_min), + INT2NUM(tm.tm_sec), + }; + return rb_rescue2(time_utc_new, (VALUE)args, time_utc_new_rescue, Qnil, + rb_eArgError, 0); } static VALUE diff --git a/test/openssl/test_asn1.rb b/test/openssl/test_asn1.rb index df8b0accb36c84..5978ecf6736e10 100644 --- a/test/openssl/test_asn1.rb +++ b/test/openssl/test_asn1.rb @@ -426,17 +426,28 @@ def test_utctime OpenSSL::ASN1::UTCTime.new(Time.new(2049, 12, 31, 23, 0, 0, "-04:00")).to_der } - # not implemented + # UTC offset (BER): ASN1_TIME_to_tm() may or may not support it # decode_test B(%w{ 17 11 }) + "500908234339+0930".b, # OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 39, "+09:30")) # decode_test B(%w{ 17 0F }) + "5009082343-0930".b, # OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 0, "-09:30")) - # assert_raise(OpenSSL::ASN1::ASN1Error) { - # OpenSSL::ASN1.decode(B(%w{ 17 0C }) + "500908234339".b) - # } - # assert_raise(OpenSSL::ASN1::ASN1Error) { - # OpenSSL::ASN1.decode(B(%w{ 17 0D }) + "500908234339Y".b) - # } + + # Seconds is omitted (BER) + # decode_test B(%w{ 18 0D }) + "201612081934Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 0)) + + # Fractional seconds is not allowed in UTCTime + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 17 0F }) + "160908234339.5Z".b) + } + + # Missing "Z" + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 17 0C }) + "500908234339".b) + } + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 17 0D }) + "500908234339Y".b) + } end def test_generalizedtime @@ -444,24 +455,46 @@ def test_generalizedtime OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 29)) encode_decode_test B(%w{ 18 0F }) + "99990908234339Z".b, OpenSSL::ASN1::GeneralizedTime.new(Time.utc(9999, 9, 8, 23, 43, 39)) - # not implemented + + # Fractional seconds (DER). Not supported by ASN1_TIME_to_tm() + # because struct tm cannot store it. + # encode_decode_test B(%w{ 18 11 }) + "20161208193439.5Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 39.5)) + + # UTC offset (BER): ASN1_TIME_to_tm() may or may not support it # decode_test B(%w{ 18 13 }) + "20161208193439+0930".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 39, "+09:30")) # decode_test B(%w{ 18 11 }) + "201612081934-0930".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:30")) # decode_test B(%w{ 18 11 }) + "201612081934-09".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:00")) + + # Minutes and seconds are omitted (BER) + # decode_test B(%w{ 18 0B }) + "2016120819Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 0, 0)) + # Fractional hours (BER) # decode_test B(%w{ 18 0D }) + "2016120819.5Z".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0)) + # Fractional hours with "," as the decimal separator (BER) # decode_test B(%w{ 18 0D }) + "2016120819,5Z".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0)) + + # Seconds is omitted (BER) + # decode_test B(%w{ 18 0D }) + "201612081934Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 0)) + # Fractional minutes (BER) # decode_test B(%w{ 18 0F }) + "201612081934.5Z".b, # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 30)) - # decode_test B(%w{ 18 11 }) + "20161208193439.5Z".b, - # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 39.5)) - # assert_raise(OpenSSL::ASN1::ASN1Error) { - # OpenSSL::ASN1.decode(B(%w{ 18 0D }) + "201612081934Y".b) - # } + + # Missing "Z" + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 18 0F }) + "20161208193429Y".b) + } + + # Encoding year out of range + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1::GeneralizedTime.new(Time.utc(10000, 9, 8, 23, 43, 39)).to_der + } end def test_basic_asn1data From ea415e9636d3be8890d5dc97cf0b67fc95403a46 Mon Sep 17 00:00:00 2001 From: Daisuke Aritomo Date: Thu, 4 Dec 2025 21:52:53 +0900 Subject: [PATCH 7/7] [ruby/net-http] open: Never call Timeout.timeout in rescue clause The try-open_timeout-then-fallback-to-timeout introduced in https://github.com/ruby/net-http/commit/1903cedd8cd0 works well, but when it errors due to any reason in Rubies which do not support `open_timeout`, it spits the rescued ArgumentError that is unrelated to user code and not actionable. Net::HTTP.start('foo.bar', 80) /.../net-http-0.8.0/lib/net/http.rb:1691:in 'TCPSocket#initialize': Failed to open TCP connection to foo.bar:80 (getaddrinfo(3): nodename nor servname provided, or not known) (Socket::ResolutionError) from /.../net-http-0.8.0/lib/net/http.rb:1691:in 'IO.open' from /.../net-http-0.8.0/lib/net/http.rb:1691:in 'block in Net::HTTP#connect' from /.../timeout-0.4.4/lib/timeout.rb:188:in 'block in Timeout.timeout' from /.../timeout-0.4.4/lib/timeout.rb:195:in 'Timeout.timeout' from /.../net-http-0.8.0/lib/net/http.rb:1690:in 'Net::HTTP#connect' from /.../net-http-0.8.0/lib/net/http.rb:1655:in 'Net::HTTP#do_start' from /.../net-http-0.8.0/lib/net/http.rb:1635:in 'Net::HTTP#start' from /.../net-http-0.8.0/lib/net/http.rb:1064:in 'Net::HTTP.start' (snip) /.../net-http-0.8.0/lib/net/http.rb:1682:in 'TCPSocket#initialize': unknown keyword: :open_timeout (ArgumentError) sock = TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ from /.../net-http-0.8.0/lib/net/http.rb:1682:in 'IO.open' from /.../net-http-0.8.0/lib/net/http.rb:1682:in 'Net::HTTP#connect' from /.../net-http-0.8.0/lib/net/http.rb:1655:in 'Net::HTTP#do_start' from /.../net-http-0.8.0/lib/net/http.rb:1635:in 'Net::HTTP#start' from /.../net-http-0.8.0/lib/net/http.rb:1064:in 'Net::HTTP.start' (snip) ... 8 levels... This patch suppresses the ArgumentError by moving the retry out of the rescue clause. https://github.com/ruby/net-http/commit/86232d62f5 --- lib/net/http.rb | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index 4205d414609bf0..8a4ff481872abc 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1674,30 +1674,7 @@ def connect debug "opening connection to #{conn_addr}:#{conn_port}..." begin - s = - case @tcpsocket_supports_open_timeout - when nil, true - begin - # Use built-in timeout in TCPSocket.open if available - sock = TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) - @tcpsocket_supports_open_timeout = true - sock - rescue ArgumentError => e - raise if !(e.message.include?('unknown keyword: :open_timeout') || e.message.include?('wrong number of arguments (given 5, expected 2..4)')) - @tcpsocket_supports_open_timeout = false - - # Fallback to Timeout.timeout if TCPSocket.open does not support open_timeout - Timeout.timeout(@open_timeout, Net::OpenTimeout) { - TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) - } - end - when false - # The current Ruby is known to not support TCPSocket(open_timeout:). - # Directly fall back to Timeout.timeout to avoid performance penalty incured by rescue. - Timeout.timeout(@open_timeout, Net::OpenTimeout) { - TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) - } - end + s = timeouted_connect(conn_addr, conn_port) rescue => e e = Net::OpenTimeout.new(e) if e.is_a?(Errno::ETIMEDOUT) # for compatibility with previous versions raise e, "Failed to open TCP connection to " + @@ -1795,6 +1772,29 @@ def connect end private :connect + def timeouted_connect(conn_addr, conn_port) + if @tcpsocket_supports_open_timeout == nil || @tcpsocket_supports_open_timeout == true + # Try to use built-in open_timeout in TCPSocket.open if: + # - The current Ruby runtime is known to support it, or + # - It is unknown whether the current Ruby runtime supports it (so we'll try). + begin + sock = TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) + @tcpsocket_supports_open_timeout = true + return sock + rescue ArgumentError => e + raise if !(e.message.include?('unknown keyword: :open_timeout') || e.message.include?('wrong number of arguments (given 5, expected 2..4)')) + @tcpsocket_supports_open_timeout = false + end + end + + # This Ruby runtime is known not to support `TCPSocket(open_timeout:)`. + # Directly fall back to Timeout.timeout to avoid performance penalty incured by rescue. + Timeout.timeout(@open_timeout, Net::OpenTimeout) { + TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) + } + end + private :timeouted_connect + def on_connect end private :on_connect