diff options
63 files changed, 749 insertions, 212 deletions
diff --git a/.github/workflows/check_branch.yml b/.github/workflows/check_branch.yml index 40292838f2..5bc20a5541 100644 --- a/.github/workflows/check_branch.yml +++ b/.github/workflows/check_branch.yml @@ -10,7 +10,7 @@ name: Pull Request on: [pull_request] jobs: check_branch: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Check if branch is ruby_2_7 run: | diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 6cdbdc272b..357be970a4 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -19,17 +19,9 @@ jobs: run: | sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate - # Not using official actions/checkout because it's unstable and sometimes doesn't work for a fork. - - name: Checkout ruby - run: | - git clone --single-branch --shallow-since=yesterday --branch=${GITHUB_REF#refs/heads/} https://github.com/${{ github.repository }} src - git -C src reset --hard "$GITHUB_SHA" - if: github.event_name == 'push' - - name: Checkout a pull request - run: | - git clone --single-branch --shallow-since=yesterday --branch=${{ github.event.pull_request.head.ref }} https://github.com/${{ github.event.pull_request.head.repo.full_name }} src - git -C src reset --hard ${{ github.event.pull_request.head.sha }} - if: github.event_name == 'pull_request' + - uses: actions/checkout@v2 + with: + path: src - run: ./src/tool/actions-commit-info.sh id: commit_info - name: Install libraries @@ -44,7 +36,7 @@ jobs: - name: Autoconf run: | cd src - autoconf + autoreconf -i - name: Configure run: | mkdir build diff --git a/.github/workflows/mjit.yml b/.github/workflows/mjit.yml index 5b662caa14..5dc02ccb75 100644 --- a/.github/workflows/mjit.yml +++ b/.github/workflows/mjit.yml @@ -13,7 +13,7 @@ jobs: test_task: [ "check" ] # to make job names consistent jit_opts: [ "--jit", "--jit-wait" ] fail-fast: false - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 if: "!contains(github.event.head_commit.message, '[ci skip]')" steps: - name: Install libraries @@ -21,17 +21,9 @@ jobs: set -x sudo apt-get update -q || : sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev bison autoconf ruby - # Not using official actions/checkout because it's unstable and sometimes doesn't work for a fork. - - name: Checkout ruby - run: | - git clone --single-branch --shallow-since=yesterday --branch=${GITHUB_REF#refs/heads/} https://github.com/${{ github.repository }} src - git -C src reset --hard "$GITHUB_SHA" - if: github.event_name == 'push' - - name: Checkout a pull request - run: | - git clone --single-branch --shallow-since=yesterday --branch=${{ github.event.pull_request.head.ref }} https://github.com/${{ github.event.pull_request.head.repo.full_name }} src - git -C src reset --hard ${{ github.event.pull_request.head.sha }} - if: github.event_name == 'pull_request' + - uses: actions/checkout@v2 + with: + path: src - run: ./src/tool/actions-commit-info.sh id: commit_info - name: Fixed world writable dirs @@ -42,7 +34,7 @@ jobs: run: | echo "JOBS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV - name: Autoconf - run: cd src && exec autoconf + run: cd src && exec autoreconf -i - name: configure run: | mkdir build diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 68f28d7b40..526fc47ce3 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: test_task: [ "check", "test-bundler", "test-bundled-gems" ] - os: [ubuntu-latest, ubuntu-16.04] + os: [ubuntu-20.04] exclude: - test_task: test-bundler os: ubuntu-16.04 @@ -52,17 +52,9 @@ jobs: set -x sudo apt-get update -q || : sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev bison autoconf ruby - # Not using official actions/checkout because it's unstable and sometimes doesn't work for a fork. - - name: Checkout ruby - run: | - git clone --single-branch --shallow-since=yesterday --branch=${GITHUB_REF#refs/heads/} https://github.com/${{ github.repository }} src - git -C src reset --hard "$GITHUB_SHA" - if: github.event_name == 'push' - - name: Checkout a pull request - run: | - git clone --single-branch --shallow-since=yesterday --branch=${{ github.event.pull_request.head.ref }} https://github.com/${{ github.event.pull_request.head.repo.full_name }} src - git -C src reset --hard ${{ github.event.pull_request.head.sha }} - if: github.event_name == 'pull_request' + - uses: actions/checkout@v2 + with: + path: src - run: ./src/tool/actions-commit-info.sh id: commit_info - name: Fixed world writable dirs @@ -73,7 +65,7 @@ jobs: run: | echo "JOBS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV - name: Autoconf - run: cd src && exec autoconf + run: cd src && exec autoreconf -i - name: configure run: | mkdir build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 82cca9353b..265a8a6157 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -37,18 +37,9 @@ jobs: - name: Install libraries with chocolatey run: | choco install --no-progress openssl winflexbison3 - # Not using official actions/checkout because it's unstable and sometimes doesn't work for a fork. - - name: Checkout ruby - run: | - git clone --single-branch --shallow-since=yesterday --branch=${GITHUB_REF#refs/heads/} https://github.com/${{ github.repository }} src - git -C src reset --hard ${{ github.sha }} - if: github.event_name == 'push' - shell: bash - - name: Checkout a pull request - run: | - git clone --single-branch --shallow-since=yesterday --branch=${{ github.event.pull_request.head.ref }} https://github.com/${{ github.event.pull_request.head.repo.full_name }} src - git -C src reset --hard ${{ github.event.pull_request.head.sha }} - if: github.event_name == 'pull_request' + - uses: actions/checkout@v2 + with: + path: src - run: ./src/tool/actions-commit-info.sh shell: bash id: commit_info diff --git a/appveyor.yml b/appveyor.yml index 7909de9e6b..d13ec885d1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,7 @@ platform: - x64 environment: ruby_version: "24-%Platform%" - zlib_version: "1.2.11" + zlib_version: "1.2.13" matrix: - build: vs vs: 120 diff --git a/configure.ac b/configure.ac index 826a688871..4395a2b156 100644 --- a/configure.ac +++ b/configure.ac @@ -2729,6 +2729,14 @@ AS_IF([test "$with_dln_a_out" != yes], [ : ${LDFLAGS=""} : ${LIBPATHENV=DYLD_FALLBACK_LIBRARY_PATH} : ${PRELOADENV=DYLD_INSERT_LIBRARIES} + AS_IF([test x"$enable_shared" = xyes], [ + # Resolve symbols from libruby.dylib when --enable-shared + EXTDLDFLAGS='$(LIBRUBYARG_SHARED)' + ], [test "x$EXTSTATIC" = x], [ + # When building exts as bundles, a mach-o bundle needs to know its loader + # program to bind symbols from the ruby executable + EXTDLDFLAGS="-bundle_loader '\$(BUILTRUBY)'" + ]) rb_cv_dlopen=yes], [aix*], [ : ${LDSHARED='$(CC)'} AS_IF([test "$GCC" = yes], [ @@ -2765,15 +2773,26 @@ AS_IF([test "$with_dln_a_out" != yes], [ AS_IF([test "$rb_cv_dlopen" = yes], [ AS_CASE(["$target_os"], [darwin*], [ + AC_SUBST(ADDITIONAL_DLDFLAGS, "") for flag in \ - "-undefined dynamic_lookup" \ "-multiply_defined suppress" \ + "-undefined dynamic_lookup" \ ; do - test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`" - RUBY_TRY_LDFLAGS([$flag], [], [flag=]) - AS_IF([test "x$flag" != x], [ - RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag]) - ]) + test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`" + RUBY_TRY_LDFLAGS([$flag], [], [$flag=]) + AS_IF([test x"$flag" = x], [continue]) + + AC_MSG_CHECKING([whether $flag is accepted for bundle]) + : > conftest.c + AS_IF([${LDSHARED/'$(CC)'/$CC} -o conftest.bundle $flag conftest.c >/dev/null 2>conftest.err && + test ! -s conftest.err], [ + AC_MSG_RESULT([yes]) + RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag]) + ], [ + AC_MSG_RESULT([no]) + RUBY_APPEND_OPTIONS(ADDITIONAL_DLDFLAGS, [$flag]) + ]) + rm -fr conftest.* done ]) ]) @@ -1796,7 +1796,7 @@ rb_fiber_new(rb_block_call_func_t func, VALUE obj) return fiber_initialize(fiber_alloc(rb_cFiber), rb_proc_new(func, obj), &shared_fiber_pool); } -static void rb_fiber_terminate(rb_fiber_t *fiber, int need_interrupt); +NORETURN(static void rb_fiber_terminate(rb_fiber_t *fiber, int need_interrupt)); #define PASS_KW_SPLAT (rb_empty_keyword_given_p() ? RB_PASS_EMPTY_KEYWORDS : rb_keyword_given_p()) @@ -2090,6 +2090,7 @@ rb_fiber_terminate(rb_fiber_t *fiber, int need_interrupt) next_fiber = return_fiber(); if (need_interrupt) RUBY_VM_SET_INTERRUPT(&next_fiber->cont.saved_ec); fiber_switch(next_fiber, 1, &value, 0, RB_NO_KEYWORDS); + ruby_stop(0); } VALUE diff --git a/enc/Makefile.in b/enc/Makefile.in index 8385236494..c389c24825 100644 --- a/enc/Makefile.in +++ b/enc/Makefile.in @@ -20,6 +20,7 @@ TRANSSODIR = $(ENCSODIR)/trans DLEXT = @DLEXT@ OBJEXT = @OBJEXT@ LIBEXT = @LIBEXT@ +EXEEXT = @EXEEXT@ TIMESTAMPDIR = $(EXTOUT)/.timestamp ENC_TRANS_D = $(TIMESTAMPDIR)/.enc-trans.time ENC_TRANS_SO_D = $(TIMESTAMPDIR)/.enc-trans.so.time @@ -33,6 +34,7 @@ RUBY_SO_NAME = @RUBY_SO_NAME@ LIBRUBY = @LIBRUBY@ LIBRUBYARG_SHARED = @LIBRUBYARG_SHARED@ LIBRUBYARG_STATIC = $(LIBRUBYARG_SHARED) +BUILTRUBY = $(topdir)/miniruby$(EXEEXT) empty = AR = @AR@ diff --git a/ext/extmk.rb b/ext/extmk.rb index 80a0a1208d..97f1ad9c39 100755 --- a/ext/extmk.rb +++ b/ext/extmk.rb @@ -408,8 +408,10 @@ if CROSS_COMPILING $ruby = $mflags.defined?("MINIRUBY") || CONFIG['MINIRUBY'] elsif sep = config_string('BUILD_FILE_SEPARATOR') $ruby = "$(topdir:/=#{sep})#{sep}miniruby" + EXEEXT -else +elsif CONFIG['EXTSTATIC'] $ruby = '$(topdir)/miniruby' + EXEEXT +else + $ruby = '$(topdir)/ruby' + EXEEXT end $ruby = [$ruby] $ruby << "-I'$(topdir)'" @@ -421,6 +423,7 @@ end topruby = $ruby $ruby = topruby.join(' ') $mflags << "ruby=#$ruby" +$builtruby = '$(topdir)/miniruby' + EXEEXT # Must be an executable path MTIMES = [__FILE__, 'rbconfig.rb', srcdir+'/lib/mkmf.rb'].collect {|f| File.mtime(f)} diff --git a/ext/openssl/History.md b/ext/openssl/History.md index 9e7ee53397..99b268182a 100644 --- a/ext/openssl/History.md +++ b/ext/openssl/History.md @@ -1,3 +1,13 @@ +Version 2.1.4 +============= + +Bug fixes +--------- + +* Do not use pkg-config if --with-openssl-dir option is specified. + [[GitHub #486]](https://github.com/ruby/openssl/pull/486) + + Version 2.1.3 ============= diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 7e817ae2da..f4212bd450 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -14,7 +14,7 @@ require "mkmf" require File.expand_path('../deprecation', __FILE__) -dir_config("openssl") +dir_config_given = dir_config("openssl").any? dir_config("kerberos") Logging::message "=== OpenSSL for Ruby configurator ===\n" @@ -88,7 +88,7 @@ def find_openssl_library end Logging::message "=== Checking for required stuff... ===\n" -pkg_config_found = pkg_config("openssl") && have_header("openssl/ssl.h") +pkg_config_found = !dir_config_given && pkg_config("openssl") && have_header("openssl/ssl.h") if !pkg_config_found && !find_openssl_library Logging::message "=== Checking for required stuff failed. ===\n" diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec index c22eece7d6..82a91f4173 100644 --- a/ext/openssl/openssl.gemspec +++ b/ext/openssl/openssl.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = "openssl" - spec.version = "2.1.3" + spec.version = "2.1.4" spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"] spec.email = ["ruby-core@ruby-lang.org"] spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.} @@ -18,7 +18,7 @@ Gem::Specification.new do |spec| spec.required_ruby_version = ">= 2.3.0" spec.add_runtime_dependency "ipaddr" - spec.add_development_dependency "rake" + spec.add_development_dependency "rake", ">= 11.2.0" spec.add_development_dependency "rake-compiler" spec.add_development_dependency "test-unit", "~> 3.0" spec.add_development_dependency "rdoc" diff --git a/ext/openssl/ossl_version.h b/ext/openssl/ossl_version.h index edbd8ce3fa..70182a71e7 100644 --- a/ext/openssl/ossl_version.h +++ b/ext/openssl/ossl_version.h @@ -10,6 +10,6 @@ #if !defined(_OSSL_VERSION_H_) #define _OSSL_VERSION_H_ -#define OSSL_VERSION "2.1.3" +#define OSSL_VERSION "2.1.4" #endif /* _OSSL_VERSION_H_ */ diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 5c8aab24af..82e9fc42da 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -285,6 +285,7 @@ static VALUE rb_gzreader_readlines(int, VALUE*, VALUE); * - Zlib::MemError * - Zlib::BufError * - Zlib::VersionError + * - Zlib::InProgressError * * (if you have GZIP_SUPPORT) * - Zlib::GzipReader @@ -301,7 +302,7 @@ void Init_zlib(void); /*--------- Exceptions --------*/ static VALUE cZError, cStreamEnd, cNeedDict; -static VALUE cStreamError, cDataError, cMemError, cBufError, cVersionError; +static VALUE cStreamError, cDataError, cMemError, cBufError, cVersionError, cInProgressError; static void raise_zlib_error(int err, const char *msg) @@ -530,6 +531,7 @@ struct zstream { unsigned long flags; VALUE buf; VALUE input; + VALUE mutex; z_stream stream; const struct zstream_funcs { int (*reset)(z_streamp); @@ -538,13 +540,14 @@ struct zstream { } *func; }; -#define ZSTREAM_FLAG_READY 0x1 -#define ZSTREAM_FLAG_IN_STREAM 0x2 -#define ZSTREAM_FLAG_FINISHED 0x4 -#define ZSTREAM_FLAG_CLOSING 0x8 -#define ZSTREAM_FLAG_GZFILE 0x10 /* disallows yield from expand_buffer for +#define ZSTREAM_FLAG_READY (1 << 0) +#define ZSTREAM_FLAG_IN_STREAM (1 << 1) +#define ZSTREAM_FLAG_FINISHED (1 << 2) +#define ZSTREAM_FLAG_CLOSING (1 << 3) +#define ZSTREAM_FLAG_GZFILE (1 << 4) /* disallows yield from expand_buffer for gzip*/ -#define ZSTREAM_FLAG_UNUSED 0x20 +#define ZSTREAM_IN_PROGRESS (1 << 5) +#define ZSTREAM_FLAG_UNUSED (1 << 6) #define ZSTREAM_READY(z) ((z)->flags |= ZSTREAM_FLAG_READY) #define ZSTREAM_IS_READY(z) ((z)->flags & ZSTREAM_FLAG_READY) @@ -571,7 +574,9 @@ static const struct zstream_funcs inflate_funcs = { }; struct zstream_run_args { - struct zstream * z; + struct zstream *const z; + Bytef *src; + long len; int flush; /* stream flush value for inflate() or deflate() */ int interrupt; /* stop processing the stream and return to ruby */ int jump_state; /* for buffer expansion block break or exception */ @@ -602,6 +607,7 @@ zstream_init(struct zstream *z, const struct zstream_funcs *func) z->flags = 0; z->buf = Qnil; z->input = Qnil; + z->mutex = rb_mutex_new(); z->stream.zalloc = zlib_mem_alloc; z->stream.zfree = zlib_mem_free; z->stream.opaque = Z_NULL; @@ -631,7 +637,9 @@ zstream_expand_buffer(struct zstream *z) rb_obj_reveal(z->buf, rb_cString); + rb_mutex_unlock(z->mutex); rb_protect(rb_yield, z->buf, &state); + rb_mutex_lock(z->mutex); z->buf = Qnil; zstream_expand_buffer_into(z, ZSTREAM_AVAIL_OUT_STEP_MAX); @@ -1024,19 +1032,18 @@ zstream_unblock_func(void *ptr) args->interrupt = 1; } -static void -zstream_run(struct zstream *z, Bytef *src, long len, int flush) +static VALUE +zstream_run_try(VALUE value_arg) { - struct zstream_run_args args; + struct zstream_run_args *args = (struct zstream_run_args *)value_arg; + struct zstream *z = args->z; + Bytef *src = args->src; + long len = args->len; + int flush = args->flush; + int err; VALUE old_input = Qnil; - args.z = z; - args.flush = flush; - args.interrupt = 0; - args.jump_state = 0; - args.stream_output = !ZSTREAM_IS_GZFILE(z) && rb_block_given_p(); - if (NIL_P(z->input) && len == 0) { z->stream.next_in = (Bytef*)""; z->stream.avail_in = 0; @@ -1058,14 +1065,20 @@ zstream_run(struct zstream *z, Bytef *src, long len, int flush) loop: #ifndef RB_NOGVL_UBF_ASYNC_SAFE - err = (int)(VALUE)rb_thread_call_without_gvl(zstream_run_func, (void *)&args, - zstream_unblock_func, (void *)&args); + err = (int)(VALUE)rb_thread_call_without_gvl(zstream_run_func, (void *)args, + zstream_unblock_func, (void *)args); #else - err = (int)(VALUE)rb_nogvl(zstream_run_func, (void *)&args, - zstream_unblock_func, (void *)&args, + err = (int)(VALUE)rb_nogvl(zstream_run_func, (void *)args, + zstream_unblock_func, (void *)args, RB_NOGVL_UBF_ASYNC_SAFE); #endif + /* retry if no exception is thrown */ + if (err == Z_OK && args->interrupt) { + args->interrupt = 0; + goto loop; + } + if (flush != Z_FINISH && err == Z_BUF_ERROR && z->stream.avail_out > 0) { z->flags |= ZSTREAM_FLAG_IN_STREAM; @@ -1099,10 +1112,52 @@ loop: rb_gc_force_recycle(old_input); } - if (args.jump_state) - rb_jump_tag(args.jump_state); + if (args->jump_state) + rb_jump_tag(args->jump_state); + return Qnil; +} + +static VALUE +zstream_run_ensure(VALUE value_arg) +{ + struct zstream_run_args *args = (struct zstream_run_args *)value_arg; + + /* Remove ZSTREAM_IN_PROGRESS flag to signal that this zstream is not in use. */ + args->z->flags &= ~ZSTREAM_IN_PROGRESS; + + return Qnil; +} + +static VALUE +zstream_run_synchronized(VALUE value_arg) +{ + struct zstream_run_args *args = (struct zstream_run_args *)value_arg; + + /* Cannot start zstream while it is in progress. */ + if (args->z->flags & ZSTREAM_IN_PROGRESS) { + rb_raise(cInProgressError, "zlib stream is in progress"); + } + args->z->flags |= ZSTREAM_IN_PROGRESS; + + rb_ensure(zstream_run_try, value_arg, zstream_run_ensure, value_arg); + + return Qnil; } +static void +zstream_run(struct zstream *z, Bytef *src, long len, int flush) +{ + struct zstream_run_args args = { + .z = z, + .src = src, + .len = len, + .flush = flush, + .interrupt = 0, + .jump_state = 0, + .stream_output = !ZSTREAM_IS_GZFILE(z) && rb_block_given_p(), + }; + rb_mutex_synchronize(z->mutex, zstream_run_synchronized, (VALUE)&args); +} static VALUE zstream_sync(struct zstream *z, Bytef *src, long len) { @@ -1148,6 +1203,7 @@ zstream_mark(void *p) struct zstream *z = p; rb_gc_mark(z->buf); rb_gc_mark(z->input); + rb_gc_mark(z->mutex); } static void @@ -4471,6 +4527,7 @@ Init_zlib(void) cMemError = rb_define_class_under(mZlib, "MemError", cZError); cBufError = rb_define_class_under(mZlib, "BufError", cZError); cVersionError = rb_define_class_under(mZlib, "VersionError", cZError); + cInProgressError = rb_define_class_under(mZlib, "InProgressError", cZError); rb_define_module_function(mZlib, "zlib_version", rb_zlib_version, 0); rb_define_module_function(mZlib, "adler32", rb_zlib_adler32, -1); @@ -4778,6 +4835,7 @@ Init_zlib(void) * - Zlib::MemError * - Zlib::BufError * - Zlib::VersionError + * - Zlib::InProgressError * */ @@ -4853,6 +4911,20 @@ Init_zlib(void) */ /* + * Document-class: Zlib::InProgressError + * + * Subclass of Zlib::Error. This error is raised when the zlib + * stream is currently in progress. + * + * For example: + * + * inflater = Zlib::Inflate.new + * inflater.inflate(compressed) do + * inflater.inflate(compressed) # Raises Zlib::InProgressError + * end + */ + +/* * Document-class: Zlib::GzipFile::Error * * Base class of errors that occur when processing GZIP files. @@ -253,6 +253,46 @@ rb_str_encode_ospath(VALUE path) #ifdef __APPLE__ # define NORMALIZE_UTF8PATH 1 + +# ifdef HAVE_WORKING_FORK +static void +rb_CFString_class_initialize_before_fork(void) +{ + /* + * Since macOS 13, CFString family API used in + * rb_str_append_normalized_ospath may internally use Objective-C classes + * (NSTaggedPointerString and NSPlaceholderMutableString) for small strings. + * + * On the other hand, Objective-C classes should not be used for the first + * time in a fork()'ed but not exec()'ed process. Violations for this rule + * can result deadlock during class initialization, so Objective-C runtime + * conservatively crashes on such cases by default. + * + * Therefore, we need to use CFString API to initialize Objective-C classes + * used internally *before* fork(). + * + * For future changes, please note that this initialization process cannot + * be done in ctor because NSTaggedPointerString in CoreFoundation is enabled + * after CFStringInitializeTaggedStrings(), which is called during loading + * Objective-C runtime after ctor. + * For more details, see https://bugs.ruby-lang.org/issues/18912 + */ + + /* Enough small but non-empty ASCII string to fit in NSTaggedPointerString. */ + const char small_str[] = "/"; + long len = sizeof(small_str) - 1; + + const CFAllocatorRef alloc = kCFAllocatorDefault; + CFStringRef s = CFStringCreateWithBytesNoCopy(alloc, + (const UInt8 *)small_str, + len, kCFStringEncodingUTF8, + FALSE, kCFAllocatorNull); + CFMutableStringRef m = CFStringCreateMutableCopy(alloc, len, s); + CFRelease(m); + CFRelease(s); +} +# endif + static VALUE rb_str_append_normalized_ospath(VALUE str, const char *ptr, long len) { @@ -6455,6 +6495,10 @@ const char ruby_null_device[] = void Init_File(void) { +#if defined(__APPLE__) && defined(HAVE_WORKING_FORK) + rb_CFString_class_initialize_before_fork(); +#endif + VALUE separator; rb_mFileTest = rb_define_module("FileTest"); @@ -11022,6 +11022,13 @@ nogvl_fcopyfile(struct copy_stream_struct *stp) return 0; if (lseek(stp->dst_fd, 0, SEEK_CUR) > (off_t)0) /* if dst IO was already written */ return 0; + if (fcntl(stp->dst_fd, F_GETFL) & O_APPEND) { + /* fcopyfile(3) appends src IO to dst IO and then truncates + * dst IO to src IO's original size. */ + off_t end = lseek(stp->dst_fd, 0, SEEK_END); + lseek(stp->dst_fd, 0, SEEK_SET); + if (end > (off_t)0) return 0; + } if (src_offset > (off_t)0) { off_t r; diff --git a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb index 07ef4391c0..bc7c9c8568 100644 --- a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +++ b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb @@ -3,8 +3,8 @@ module Bundler::URI class RFC3986_Parser # :nodoc: # Bundler::URI defined in RFC3986 # this regexp is modified not to host is not empty string - RFC3986_URI = /\A(?<Bundler::URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ - RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ + RFC3986_URI = /\A(?<Bundler::URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*+):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/ + RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/ attr_reader :regexp def initialize diff --git a/lib/bundler/vendor/uri/lib/uri/version.rb b/lib/bundler/vendor/uri/lib/uri/version.rb index 56177ef194..c162bf6368 100644 --- a/lib/bundler/vendor/uri/lib/uri/version.rb +++ b/lib/bundler/vendor/uri/lib/uri/version.rb @@ -1,6 +1,6 @@ module Bundler::URI # :stopdoc: - VERSION_CODE = '001000'.freeze + VERSION_CODE = '00100002'.freeze VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze # :startdoc: end diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb index 6b0d89ca3b..1a9c1a82c1 100644 --- a/lib/cgi/cookie.rb +++ b/lib/cgi/cookie.rb @@ -40,6 +40,10 @@ class CGI class Cookie < Array @@accept_charset="UTF-8" unless defined?(@@accept_charset) + TOKEN_RE = %r"\A[[!-~]&&[^()<>@,;:\\\"/?=\[\]{}]]+\z" + PATH_VALUE_RE = %r"\A[[ -~]&&[^;]]*\z" + DOMAIN_VALUE_RE = %r"\A(?<label>(?!-)[-A-Za-z0-9]+(?<!-))(?:\.\g<label>)*\z" + # Create a new CGI::Cookie object. # # :call-seq: @@ -72,8 +76,8 @@ class CGI @domain = nil @expires = nil if name.kind_of?(String) - @name = name - @path = (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "") + self.name = name + self.path = (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "") @secure = false @httponly = false return super(value) @@ -84,11 +88,11 @@ class CGI raise ArgumentError, "`name' required" end - @name = options["name"] + self.name = options["name"] value = Array(options["value"]) # simple support for IE - @path = options["path"] || (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "") - @domain = options["domain"] + self.path = options["path"] || (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "") + self.domain = options["domain"] @expires = options["expires"] @secure = options["secure"] == true @httponly = options["httponly"] == true @@ -97,11 +101,35 @@ class CGI end # Name of this cookie, as a +String+ - attr_accessor :name + attr_reader :name + # Set name of this cookie + def name=(str) + if str and !TOKEN_RE.match?(str) + raise ArgumentError, "invalid name: #{str.dump}" + end + @name = str + end + # Path for which this cookie applies, as a +String+ - attr_accessor :path + attr_reader :path + # Set path for which this cookie applies + def path=(str) + if str and !PATH_VALUE_RE.match?(str) + raise ArgumentError, "invalid path: #{str.dump}" + end + @path = str + end + # Domain for which this cookie applies, as a +String+ - attr_accessor :domain + attr_reader :domain + # Set domain for which this cookie applies + def domain=(str) + if str and ((str = str.b).bytesize > 255 or !DOMAIN_VALUE_RE.match?(str)) + raise ArgumentError, "invalid domain: #{str.dump}" + end + @domain = str + end + # Time at which this cookie expires, as a +Time+ attr_accessor :expires # True if this cookie is secure; false otherwise diff --git a/lib/cgi/core.rb b/lib/cgi/core.rb index bec76e0749..62e606837a 100644 --- a/lib/cgi/core.rb +++ b/lib/cgi/core.rb @@ -188,17 +188,28 @@ class CGI # Using #header with the HTML5 tag maker will create a <header> element. alias :header :http_header + def _no_crlf_check(str) + if str + str = str.to_s + raise "A HTTP status or header field must not include CR and LF" if str =~ /[\r\n]/ + str + else + nil + end + end + private :_no_crlf_check + def _header_for_string(content_type) #:nodoc: buf = ''.dup if nph?() - buf << "#{$CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'} 200 OK#{EOL}" + buf << "#{_no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0'} 200 OK#{EOL}" buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}" - buf << "Server: #{$CGI_ENV['SERVER_SOFTWARE']}#{EOL}" + buf << "Server: #{_no_crlf_check($CGI_ENV['SERVER_SOFTWARE'])}#{EOL}" buf << "Connection: close#{EOL}" end - buf << "Content-Type: #{content_type}#{EOL}" + buf << "Content-Type: #{_no_crlf_check(content_type)}#{EOL}" if @output_cookies - @output_cookies.each {|cookie| buf << "Set-Cookie: #{cookie}#{EOL}" } + @output_cookies.each {|cookie| buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}" } end return buf end # _header_for_string @@ -213,9 +224,9 @@ class CGI ## NPH options.delete('nph') if defined?(MOD_RUBY) if options.delete('nph') || nph?() - protocol = $CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0' + protocol = _no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0' status = options.delete('status') - status = HTTP_STATUS[status] || status || '200 OK' + status = HTTP_STATUS[status] || _no_crlf_check(status) || '200 OK' buf << "#{protocol} #{status}#{EOL}" buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}" options['server'] ||= $CGI_ENV['SERVER_SOFTWARE'] || '' @@ -223,38 +234,38 @@ class CGI end ## common headers status = options.delete('status') - buf << "Status: #{HTTP_STATUS[status] || status}#{EOL}" if status + buf << "Status: #{HTTP_STATUS[status] || _no_crlf_check(status)}#{EOL}" if status server = options.delete('server') - buf << "Server: #{server}#{EOL}" if server + buf << "Server: #{_no_crlf_check(server)}#{EOL}" if server connection = options.delete('connection') - buf << "Connection: #{connection}#{EOL}" if connection + buf << "Connection: #{_no_crlf_check(connection)}#{EOL}" if connection type = options.delete('type') - buf << "Content-Type: #{type}#{EOL}" #if type + buf << "Content-Type: #{_no_crlf_check(type)}#{EOL}" #if type length = options.delete('length') - buf << "Content-Length: #{length}#{EOL}" if length + buf << "Content-Length: #{_no_crlf_check(length)}#{EOL}" if length language = options.delete('language') - buf << "Content-Language: #{language}#{EOL}" if language + buf << "Content-Language: #{_no_crlf_check(language)}#{EOL}" if language expires = options.delete('expires') buf << "Expires: #{CGI.rfc1123_date(expires)}#{EOL}" if expires ## cookie if cookie = options.delete('cookie') case cookie when String, Cookie - buf << "Set-Cookie: #{cookie}#{EOL}" + buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}" when Array arr = cookie - arr.each {|c| buf << "Set-Cookie: #{c}#{EOL}" } + arr.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" } when Hash hash = cookie - hash.each_value {|c| buf << "Set-Cookie: #{c}#{EOL}" } + hash.each_value {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" } end end if @output_cookies - @output_cookies.each {|c| buf << "Set-Cookie: #{c}#{EOL}" } + @output_cookies.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" } end ## other headers options.each do |key, value| - buf << "#{key}: #{value}#{EOL}" + buf << "#{_no_crlf_check(key)}: #{_no_crlf_check(value)}#{EOL}" end return buf end # _header_for_hash diff --git a/lib/cgi/version.rb b/lib/cgi/version.rb index e145a762c6..ee0c748069 100644 --- a/lib/cgi/version.rb +++ b/lib/cgi/version.rb @@ -1,3 +1,3 @@ class CGI - VERSION = "0.1.0.1" + VERSION = "0.1.0.2" end diff --git a/lib/mkmf.rb b/lib/mkmf.rb index eabccd48eb..27bc9e5986 100644 --- a/lib/mkmf.rb +++ b/lib/mkmf.rb @@ -2034,6 +2034,11 @@ sitearch = #{CONFIG['sitearch']} ruby_version = #{RbConfig::CONFIG['ruby_version']} ruby = #{$ruby.sub(%r[\A#{Regexp.quote(RbConfig::CONFIG['bindir'])}(?=/|\z)]) {'$(bindir)'}} RUBY = $(ruby#{sep}) +BUILTRUBY = #{if defined?($builtruby) && $builtruby + $builtruby + else + File.join('$(bindir)', CONFIG["RUBY_INSTALL_NAME"] + CONFIG['EXEEXT']) + end} ruby_headers = #{headers.join(' ')} RM = #{config_string('RM', &possible_command) || '$(RUBY) -run -e rm -- -f'} @@ -2552,6 +2557,7 @@ site-install-rb: install-rb $INCFLAGS << " -I$(hdrdir)/ruby/backward" unless $extmk $INCFLAGS << " -I$(hdrdir) -I$(srcdir)" $DLDFLAGS = with_config("dldflags", arg_config("DLDFLAGS", config["DLDFLAGS"])).dup + config_string("ADDITIONAL_DLDFLAGS") {|flags| $DLDFLAGS << " " << flags} unless $extmk $LIBEXT = config['LIBEXT'].dup $OBJEXT = config["OBJEXT"].dup $EXEEXT = config["EXEEXT"].dup diff --git a/lib/net/http/header.rb b/lib/net/http/header.rb index 8641be4eae..b99cf17c32 100644 --- a/lib/net/http/header.rb +++ b/lib/net/http/header.rb @@ -9,6 +9,8 @@ # convenient formats. # module Net::HTTPHeader + MAX_KEY_LENGTH = 1024 + MAX_FIELD_LENGTH = 65536 def initialize_http_header(initheader) @header = {} @@ -19,6 +21,12 @@ module Net::HTTPHeader warn "net/http: nil HTTP header: #{key}", uplevel: 3 if $VERBOSE else value = value.strip # raise error for invalid byte sequences + if key.to_s.bytesize > MAX_KEY_LENGTH + raise ArgumentError, "too long (#{key.bytesize} bytes) header: #{key[0, 30].inspect}..." + end + if value.to_s.bytesize > MAX_FIELD_LENGTH + raise ArgumentError, "header #{key} has too long field vallue: #{value.bytesize}" + end if value.count("\r\n") > 0 raise ArgumentError, "header #{key} has field value #{value.inspect}, this cannot include CR/LF" end diff --git a/lib/time.rb b/lib/time.rb index f27bacde65..245e1a2320 100644 --- a/lib/time.rb +++ b/lib/time.rb @@ -501,8 +501,8 @@ class Time (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+ (\d{2,})\s+ (\d{2})\s* - :\s*(\d{2})\s* - (?::\s*(\d{2}))?\s+ + :\s*(\d{2}) + (?:\s*:\s*(\d\d))?\s+ ([+-]\d{4}| UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Z])/ix =~ date # Since RFC 2822 permit comments, the regexp has no right anchor. diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb index 08539f069a..c6f40a2dfc 100644 --- a/lib/uri/rfc3986_parser.rb +++ b/lib/uri/rfc3986_parser.rb @@ -3,8 +3,8 @@ module URI class RFC3986_Parser # :nodoc: # URI defined in RFC3986 # this regexp is modified not to host is not empty string - RFC3986_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ - RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ + RFC3986_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*+):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/ + RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/ attr_reader :regexp def initialize diff --git a/lib/uri/version.rb b/lib/uri/version.rb index 4f54113393..c07f8c2878 100644 --- a/lib/uri/version.rb +++ b/lib/uri/version.rb @@ -1,6 +1,6 @@ module URI # :stopdoc: - VERSION_CODE = '001000'.freeze + VERSION_CODE = '00100002'.freeze VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze # :startdoc: end @@ -1076,7 +1076,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception) if (ftptr) load_unlock(RSTRING_PTR(path), !state); if (state) { - if (state == TAG_FATAL) { + if (state == TAG_FATAL || state == TAG_THROW) { EC_JUMP_TAG(ec, state); } else if (exception) { @@ -164,6 +164,7 @@ struct rb_method_definition_struct { BITFIELD(rb_method_type_t, type, VM_METHOD_TYPE_MINIMUM_BITS); int alias_count : 28; int complemented_count : 28; + unsigned int no_redef_warning: 1; union { rb_method_iseq_t iseq; diff --git a/missing/dtoa.c b/missing/dtoa.c index cbee13ee81..e82b60c2ce 100644 --- a/missing/dtoa.c +++ b/missing/dtoa.c @@ -1500,6 +1500,7 @@ break2: if (!*++s || !(s1 = strchr(hexdigit, *s))) goto ret0; if (*s == '0') { while (*++s == '0'); + if (!*s) goto ret; s1 = strchr(hexdigit, *s); } if (s1 != NULL) { @@ -1522,7 +1523,7 @@ break2: for (; *s && (s1 = strchr(hexdigit, *s)); ++s) { adj += aadj * ((s1 - hexdigit) & 15); if ((aadj /= 16) == 0.0) { - while (strchr(hexdigit, *++s)); + while (*++s && strchr(hexdigit, *s)); break; } } diff --git a/mjit_worker.c b/mjit_worker.c index 95518883c6..f0b73be2d0 100644 --- a/mjit_worker.c +++ b/mjit_worker.c @@ -823,8 +823,15 @@ link_o_to_so(const char **o_files, const char *so_file) NULL }; - char **args = form_args(7, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS, - options, o_files, CC_LIBS, CC_DLDFLAGS_ARGS, CC_LINKER_ARGS); +# if defined(__MACH__) + extern VALUE rb_libruby_selfpath; + const char *loader_args[] = {"-bundle_loader", StringValuePtr(rb_libruby_selfpath), NULL}; +# else + const char *loader_args[] = {NULL}; +# endif + + char **args = form_args(8, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS, + options, o_files, loader_args, CC_LIBS, CC_DLDFLAGS_ARGS, CC_LINKER_ARGS); if (args == NULL) return false; @@ -4565,6 +4565,7 @@ rb_fix_lshift(VALUE x, VALUE y) long val, width; val = NUM2LONG(x); + if (!val) return (rb_to_int(y), INT2FIX(0)); if (!FIXNUM_P(y)) return rb_big_lshift(rb_int2big(val), y); width = FIX2LONG(y); @@ -4611,6 +4612,7 @@ rb_fix_rshift(VALUE x, VALUE y) long i, val; val = FIX2LONG(x); + if (!val) return (rb_to_int(y), INT2FIX(0)); if (!FIXNUM_P(y)) return rb_big_rshift(rb_int2big(val), y); i = FIX2LONG(y); @@ -46,7 +46,6 @@ #define YYCALLOC(nelem, size) rb_parser_calloc(p, (nelem), (size)) #define YYFREE(ptr) rb_parser_free(p, (ptr)) #define YYFPRINTF rb_parser_printf -#define YYPRINT(out, tok, val) parser_token_value_print(p, (tok), &(val)) #define YY_LOCATION_PRINT(File, loc) \ rb_parser_printf(p, "%d.%d-%d.%d", \ (loc).beg_pos.lineno, (loc).beg_pos.column,\ @@ -602,7 +601,6 @@ RUBY_SYMBOL_EXPORT_END static void error_duplicate_pattern_variable(struct parser_params *p, ID id, const YYLTYPE *loc); static void error_duplicate_pattern_key(struct parser_params *p, ID id, const YYLTYPE *loc); -static void parser_token_value_print(struct parser_params *p, enum yytokentype type, const YYSTYPE *valp); static ID formal_argument(struct parser_params*, ID); static ID shadowing_lvar(struct parser_params*,ID); static void new_bv(struct parser_params*,ID); @@ -969,6 +967,35 @@ static int looking_at_eol_p(struct parser_params *p); %expect 0 %define api.pure %define parse.error verbose +%printer { +#ifndef RIPPER + rb_parser_printf(p, "%"PRIsVALUE, rb_id2str($$)); +#else + rb_parser_printf(p, "%"PRIsVALUE, RNODE($$)->nd_rval); +#endif +} tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL tOP_ASGN +%printer { +#ifndef RIPPER + rb_parser_printf(p, "%+"PRIsVALUE, $$->nd_lit); +#else + rb_parser_printf(p, "%+"PRIsVALUE, get_value($$)); +#endif +} tINTEGER tFLOAT tRATIONAL tIMAGINARY tSTRING_CONTENT tCHAR +%printer { +#ifndef RIPPER + rb_parser_printf(p, "$%ld", $$->nd_nth); +#else + rb_parser_printf(p, "%"PRIsVALUE, $$); +#endif +} tNTH_REF +%printer { +#ifndef RIPPER + rb_parser_printf(p, "$%c", (int)$$->nd_nth); +#else + rb_parser_printf(p, "%"PRIsVALUE, $$); +#endif +} tBACK_REF + %lex-param {struct parser_params *p} %parse-param {struct parser_params *p} %initial-action @@ -10424,49 +10451,6 @@ rb_parser_set_location(struct parser_params *p, YYLTYPE *yylloc) } #endif /* !RIPPER */ -static void -parser_token_value_print(struct parser_params *p, enum yytokentype type, const YYSTYPE *valp) -{ - VALUE v; - - switch (type) { - case tIDENTIFIER: case tFID: case tGVAR: case tIVAR: - case tCONSTANT: case tCVAR: case tLABEL: case tOP_ASGN: -#ifndef RIPPER - v = rb_id2str(valp->id); -#else - v = valp->node->nd_rval; -#endif - rb_parser_printf(p, "%"PRIsVALUE, v); - break; - case tINTEGER: case tFLOAT: case tRATIONAL: case tIMAGINARY: - case tSTRING_CONTENT: case tCHAR: -#ifndef RIPPER - v = valp->node->nd_lit; -#else - v = valp->val; -#endif - rb_parser_printf(p, "%+"PRIsVALUE, v); - break; - case tNTH_REF: -#ifndef RIPPER - rb_parser_printf(p, "$%ld", valp->node->nd_nth); -#else - rb_parser_printf(p, "%"PRIsVALUE, valp->val); -#endif - break; - case tBACK_REF: -#ifndef RIPPER - rb_parser_printf(p, "$%c", (int)valp->node->nd_nth); -#else - rb_parser_printf(p, "%"PRIsVALUE, valp->val); -#endif - break; - default: - break; - } -} - static int assignable0(struct parser_params *p, ID id, const char **err) { @@ -28,7 +28,7 @@ #ifdef __hpux #include <sys/pstat.h> #endif -#if defined(LOAD_RELATIVE) && defined(HAVE_DLADDR) +#if (defined(LOAD_RELATIVE) || defined(__MACH__)) && defined(HAVE_DLADDR) #include <dlfcn.h> #endif @@ -501,7 +501,7 @@ str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to) void ruby_init_loadpath(void); -#if defined(LOAD_RELATIVE) +#if defined(LOAD_RELATIVE) || defined(__MACH__) static VALUE runtime_libruby_path(void) { @@ -577,6 +577,10 @@ runtime_libruby_path(void) #define INITIAL_LOAD_PATH_MARK rb_intern_const("@gem_prelude_index") VALUE ruby_archlibdir_path, ruby_prefix_path; +#if defined(__MACH__) +// A path to libruby.dylib itself or where it's statically linked to. +VALUE rb_libruby_selfpath; +#endif void ruby_init_loadpath(void) @@ -584,6 +588,14 @@ ruby_init_loadpath(void) VALUE load_path, archlibdir = 0; ID id_initial_load_path_mark; const char *paths = ruby_initial_load_paths; +#if defined(LOAD_RELATIVE) || defined(__MACH__) + VALUE libruby_path = runtime_libruby_path(); +# if defined(__MACH__) + rb_libruby_selfpath = libruby_path; + rb_gc_register_address(&rb_libruby_selfpath); +# endif +#endif + #if defined LOAD_RELATIVE #if !defined ENABLE_MULTIARCH # define RUBY_ARCH_PATH "" @@ -597,7 +609,7 @@ ruby_init_loadpath(void) size_t baselen; const char *p; - sopath = runtime_libruby_path(); + sopath = libruby_path; libpath = RSTRING_PTR(sopath); p = strrchr(libpath, '/'); diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb index 75525d405b..8123c514b6 100644 --- a/spec/bundler/cache/git_spec.rb +++ b/spec/bundler/cache/git_spec.rb @@ -149,6 +149,9 @@ RSpec.describe "bundle cache with git" do end it "copies repository to vendor cache, including submodules" do + # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/ + system(*%W[git config --global protocol.file.allow always]) + build_git "submodule", "1.0" git = build_git "has_submodule", "1.0" do |s| diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb index 00f8e96625..c5ea2c202d 100644 --- a/spec/bundler/install/gemfile/git_spec.rb +++ b/spec/bundler/install/gemfile/git_spec.rb @@ -826,6 +826,9 @@ RSpec.describe "bundle install with git sources" do end it "ignores submodules if :submodule is not passed" do + # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/ + system(*%W[git config --global protocol.file.allow always]) + build_git "submodule", "1.0" build_git "has_submodule", "1.0" do |s| s.add_dependency "submodule" @@ -846,6 +849,9 @@ RSpec.describe "bundle install with git sources" do end it "handles repos with submodules" do + # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/ + system(*%W[git config --global protocol.file.allow always]) + build_git "submodule", "1.0" build_git "has_submodule", "1.0" do |s| s.add_dependency "submodule" diff --git a/spec/bundler/update/git_spec.rb b/spec/bundler/update/git_spec.rb index 752033c842..8a41ab86db 100644 --- a/spec/bundler/update/git_spec.rb +++ b/spec/bundler/update/git_spec.rb @@ -117,6 +117,9 @@ RSpec.describe "bundle update" do describe "with submodules" do before :each do + # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/ + system(*%W[git config --global protocol.file.allow always]) + build_repo4 do build_gem "submodule" do |s| s.write "lib/submodule.rb", "puts 'GEM'" diff --git a/spec/ruby/core/time/shared/local.rb b/spec/ruby/core/time/shared/local.rb index 43f331c4c1..997b7186f1 100644 --- a/spec/ruby/core/time/shared/local.rb +++ b/spec/ruby/core/time/shared/local.rb @@ -6,6 +6,7 @@ describe :time_local, shared: true do end end +=begin platform_is_not :windows do describe "timezone changes" do it "correctly adjusts the timezone change to 'CEST' on 'Europe/Amsterdam'" do @@ -16,6 +17,7 @@ describe :time_local, shared: true do end end end +=end end describe :time_local_10_arg, shared: true do diff --git a/spec/ruby/library/cgi/cookie/name_spec.rb b/spec/ruby/library/cgi/cookie/name_spec.rb index 14226824c8..326a43ade3 100644 --- a/spec/ruby/library/cgi/cookie/name_spec.rb +++ b/spec/ruby/library/cgi/cookie/name_spec.rb @@ -6,18 +6,18 @@ describe "CGI::Cookie#name" do cookie = CGI::Cookie.new("test-cookie") cookie.name.should == "test-cookie" - cookie = CGI::Cookie.new("name" => "another cookie") - cookie.name.should == "another cookie" + cookie = CGI::Cookie.new("name" => "another-cookie") + cookie.name.should == "another-cookie" end end describe "CGI::Cookie#name=" do it "sets self's expiration date" do cookie = CGI::Cookie.new("test-cookie") - cookie.name = "another name" - cookie.name.should == "another name" + cookie.name = "another-name" + cookie.name.should == "another-name" - cookie.name = "and one more" - cookie.name.should == "and one more" + cookie.name = "and-one-more" + cookie.name.should == "and-one-more" end end diff --git a/spec/ruby/library/cgi/cookie/parse_spec.rb b/spec/ruby/library/cgi/cookie/parse_spec.rb index 90d2c3d148..d484c7bad9 100644 --- a/spec/ruby/library/cgi/cookie/parse_spec.rb +++ b/spec/ruby/library/cgi/cookie/parse_spec.rb @@ -6,16 +6,16 @@ describe "CGI::Cookie.parse" do expected = { "test-cookie" => ["one", "two", "three"] } CGI::Cookie.parse("test-cookie=one&two&three").should == expected - expected = { "second cookie" => ["three", "four"], "first cookie" => ["one", "two"] } - CGI::Cookie.parse("first cookie=one&two;second cookie=three&four").should == expected + expected = { "second-cookie" => ["three", "four"], "first-cookie" => ["one", "two"] } + CGI::Cookie.parse("first-cookie=one&two;second-cookie=three&four").should == expected end it "does not use , for cookie separators" do expected = { - "first cookie" => ["one", "two"], - "second cookie" => ["three", "four,third_cookie=five", "six"] + "first-cookie" => ["one", "two"], + "second-cookie" => ["three", "four,third_cookie=five", "six"] } - CGI::Cookie.parse("first cookie=one&two;second cookie=three&four,third_cookie=five&six").should == expected + CGI::Cookie.parse("first-cookie=one&two;second-cookie=three&four,third_cookie=five&six").should == expected end it "unescapes the Cookie values" do @@ -1486,7 +1486,6 @@ st_shift(st_table *tab, st_data_t *key, st_data_t *value) } } st_assert(tab->num_entries == 0); - tab->entries_start = tab->entries_bound = 0; if (value != 0) *value = 0; return 0; } diff --git a/test/cgi/test_cgi_cookie.rb b/test/cgi/test_cgi_cookie.rb index 985cc0d7a1..e3ec4bea52 100644 --- a/test/cgi/test_cgi_cookie.rb +++ b/test/cgi/test_cgi_cookie.rb @@ -60,6 +60,24 @@ class CGICookieTest < Test::Unit::TestCase end + def test_cgi_cookie_new_with_domain + h = {'name'=>'name1', 'value'=>'value1'} + cookie = CGI::Cookie.new('domain'=>'a.example.com', **h) + assert_equal('a.example.com', cookie.domain) + + cookie = CGI::Cookie.new('domain'=>'1.example.com', **h) + assert_equal('1.example.com', cookie.domain, 'enhanced by RFC 1123') + + assert_raise(ArgumentError) { + CGI::Cookie.new('domain'=>'-a.example.com', **h) + } + + assert_raise(ArgumentError) { + CGI::Cookie.new('domain'=>'a-.example.com', **h) + } + end + + def test_cgi_cookie_scriptname cookie = CGI::Cookie.new('name1', 'value1') assert_equal('', cookie.path) @@ -118,6 +136,70 @@ class CGICookieTest < Test::Unit::TestCase end + def test_cgi_cookie_domain_injection_into_name + name = "a=b; domain=example.com;" + path = "/" + domain = "example.jp" + assert_raise(ArgumentError) do + CGI::Cookie.new('name' => name, + 'value' => "value", + 'domain' => domain, + 'path' => path) + end + end + + + def test_cgi_cookie_newline_injection_into_name + name = "a=b;\r\nLocation: http://example.com#" + path = "/" + domain = "example.jp" + assert_raise(ArgumentError) do + CGI::Cookie.new('name' => name, + 'value' => "value", + 'domain' => domain, + 'path' => path) + end + end + + + def test_cgi_cookie_multibyte_injection_into_name + name = "a=b;\u3042" + path = "/" + domain = "example.jp" + assert_raise(ArgumentError) do + CGI::Cookie.new('name' => name, + 'value' => "value", + 'domain' => domain, + 'path' => path) + end + end + + + def test_cgi_cookie_injection_into_path + name = "name" + path = "/; samesite=none" + domain = "example.jp" + assert_raise(ArgumentError) do + CGI::Cookie.new('name' => name, + 'value' => "value", + 'domain' => domain, + 'path' => path) + end + end + + + def test_cgi_cookie_injection_into_domain + name = "name" + path = "/" + domain = "example.jp; samesite=none" + assert_raise(ArgumentError) do + CGI::Cookie.new('name' => name, + 'value' => "value", + 'domain' => domain, + 'path' => path) + end + end + instance_methods.each do |method| private method if method =~ /^test_(.*)/ && $1 != ENV['TEST'] diff --git a/test/cgi/test_cgi_header.rb b/test/cgi/test_cgi_header.rb index bab2d0348a..ec2f4deb72 100644 --- a/test/cgi/test_cgi_header.rb +++ b/test/cgi/test_cgi_header.rb @@ -176,6 +176,14 @@ class CGIHeaderTest < Test::Unit::TestCase end + def test_cgi_http_header_crlf_injection + cgi = CGI.new + assert_raise(RuntimeError) { cgi.http_header("text/xhtml\r\nBOO") } + assert_raise(RuntimeError) { cgi.http_header("type" => "text/xhtml\r\nBOO") } + assert_raise(RuntimeError) { cgi.http_header("status" => "200 OK\r\nBOO") } + assert_raise(RuntimeError) { cgi.http_header("location" => "text/xhtml\r\nBOO") } + end + instance_methods.each do |method| private method if method =~ /^test_(.*)/ && $1 != ENV['TEST'] diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb index a740c21ae9..d084f10f79 100644 --- a/test/ripper/test_parser_events.rb +++ b/test/ripper/test_parser_events.rb @@ -1498,16 +1498,9 @@ class TestRipper::ParserEvents < Test::Unit::TestCase end def test_block_variables - assert_equal("[fcall(proc,[],&block([],[void()]))]", parse("proc{|;y|}")) - if defined?(Process::RLIMIT_AS) - dir = File.dirname(__FILE__) - as = 100 * 1024 * 1024 # 100MB - as *= 2 if RubyVM::MJIT.enabled? # space for compiler - assert_in_out_err(%W(-I#{dir} -rdummyparser), - "Process.setrlimit(Process::RLIMIT_AS,#{as}); "\ - "puts DummyParser.new('proc{|;y|!y}').parse", - ["[fcall(proc,[],&block([],[unary(!,ref(y))]))]"], [], '[ruby-dev:39423]') - end + bug4159 = '[ruby-dev:39423]' + assert_equal("[fcall(proc,[],&block([],[void()]))]", parse("proc{|;y|}"), bug4159) + assert_equal("[fcall(proc,[],&block([],[unary(!,ref(y))]))]", parse("proc{|;y|!y}"), bug4159) end def test_unterminated_regexp diff --git a/test/ruby/test_alias.rb b/test/ruby/test_alias.rb index 33fb82e1d7..1acf12f7f3 100644 --- a/test/ruby/test_alias.rb +++ b/test/ruby/test_alias.rb @@ -227,4 +227,33 @@ class TestAlias < Test::Unit::TestCase assert_equal(:foo, k.instance_method(:bar).original_name) assert_equal(:foo, name) end + + def test_alias_suppressing_redefinition + assert_in_out_err(%w[-w], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + class A + def foo; end + alias foo foo + def foo; end + end + end; + end + + def test_alias_memory_leak + assert_no_memory_leak([], "#{<<~"begin;"}", "#{<<~'end;'}", rss: true) + begin; + class A + 500.times do + 1000.times do |i| + define_method(:"foo_#{i}") {} + + alias :"foo_#{i}" :"foo_#{i}" + + remove_method :"foo_#{i}" + end + GC.start + end + end + end; + end end diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb index 9f6f19b669..ef732b9924 100644 --- a/test/ruby/test_enum.rb +++ b/test/ruby/test_enum.rb @@ -326,6 +326,17 @@ class TestEnumerable < Test::Unit::TestCase empty.first empty.block.call end; + + bug18475 = '[ruby-dev:107059]' + assert_in_out_err([], <<-'end;', [], /unexpected break/, bug18475) + e = Enumerator.new do |g| + Thread.new do + g << 1 + end.join + end + + e.first + end; end def test_sort diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 69bf7ebf0e..9efcfc76cf 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -181,6 +181,27 @@ class TestException < Test::Unit::TestCase } end + def test_catch_throw_in_require_cant_be_rescued + bug18562 = '[ruby-core:107403]' + Tempfile.create(["dep", ".rb"]) {|t| + t.puts("throw :extdep, 42") + t.close + + rescue_all = Class.new(Exception) + def rescue_all.===(_) + raise "should not reach here" + end + + v = assert_throw(:extdep, bug18562) do + require t.path + rescue rescue_all => e + assert(false, "should not reach here") + end + + assert_equal(42, v, bug18562) + } + end + def test_throw_false bug12743 = '[ruby-core:77229] [Bug #12743]' Thread.start { diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb index a7ed9ac7e0..20436eca69 100644 --- a/test/ruby/test_fiber.rb +++ b/test/ruby/test_fiber.rb @@ -318,7 +318,7 @@ class TestFiber < Test::Unit::TestCase Fiber.new {}.transfer Fiber.new { Fiber.yield } end - exit!(0) + exit!(true) end }.transfer _, status = Process.waitpid2(xpid) @@ -327,8 +327,8 @@ class TestFiber < Test::Unit::TestCase end.resume end pid, status = Process.waitpid2(pid) - assert_equal(0, status.exitstatus, bug5700) - assert_equal(false, status.signaled?, bug5700) + assert_not_predicate(status, :signaled?, bug5700) + assert_predicate(status, :success?, bug5700) end def test_exit_in_fiber diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb index 7cbf3b5a8f..9c24dac8e6 100644 --- a/test/ruby/test_float.rb +++ b/test/ruby/test_float.rb @@ -171,6 +171,24 @@ class TestFloat < Test::Unit::TestCase assert_raise(ArgumentError, n += z + "A") {Float(n)} assert_raise(ArgumentError, n += z + ".0") {Float(n)} end + + x = nil + 2000.times do + x = Float("0x"+"0"*30) + break unless x == 0.0 + end + assert_equal(0.0, x, ->{"%a" % x}) + x = nil + 2000.times do + begin + x = Float("0x1."+"0"*270) + rescue ArgumentError => e + raise unless /"0x1\.0{270}"/ =~ e.message + else + break + end + end + assert_nil(x, ->{"%a" % x}) end def test_divmod diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index 046ea40f5d..d4af130a07 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -983,6 +983,19 @@ class TestHash < Test::Unit::TestCase assert_equal("FOO", h.shift) end + def test_shift_for_empty_hash + # [ruby-dev:51159] + h = @cls[] + 100.times{|n| + while h.size < n + k = Random.rand 0..1<<30 + h[k] = 1 + end + 0 while h.shift + assert_equal({}, h) + } + end + def test_reject_bang2 assert_equal({1=>2}, @cls[1=>2,3=>4].reject! {|k, v| k + v == 7 }) assert_nil(@cls[1=>2,3=>4].reject! {|k, v| k == 5 }) diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 1e4d88ef1e..37f1477483 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -440,6 +440,18 @@ class TestIO < Test::Unit::TestCase } end + def test_copy_stream_append_to_nonempty + with_srccontent("foobar") {|src, content| + preface = 'preface' + File.write('dst', preface) + File.open('dst', 'ab') do |dst| + ret = IO.copy_stream(src, dst) + assert_equal(content.bytesize, ret) + assert_equal(preface + content, File.read("dst")) + end + } + end + def test_copy_stream_smaller with_srccontent {|src, content| diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb index 5ee12e4dbd..47f8c63077 100644 --- a/test/ruby/test_time_tz.rb +++ b/test/ruby/test_time_tz.rb @@ -7,9 +7,9 @@ class TestTimeTZ < Test::Unit::TestCase has_lisbon_tz = true force_tz_test = ENV["RUBY_FORCE_TIME_TZ_TEST"] == "yes" case RUBY_PLATFORM - when /linux/ + when /darwin|linux/ force_tz_test = true - when /darwin|freebsd|openbsd/ + when /freebsd|openbsd/ has_lisbon_tz = false force_tz_test = true end @@ -95,6 +95,9 @@ class TestTimeTZ < Test::Unit::TestCase CORRECT_KIRITIMATI_SKIP_1994 = with_tz("Pacific/Kiritimati") { Time.local(1994, 12, 31, 0, 0, 0).year == 1995 } + CORRECT_SINGAPORE_1982 = with_tz("Asia/Singapore") { + "2022g" if Time.local(1981, 12, 31, 23, 59, 59).utc_offset == 8*3600 + } def time_to_s(t) t.to_s @@ -140,9 +143,12 @@ class TestTimeTZ < Test::Unit::TestCase def test_asia_singapore with_tz(tz="Asia/Singapore") { - assert_time_constructor(tz, "1981-12-31 23:59:59 +0730", :local, [1981,12,31,23,59,59]) - assert_time_constructor(tz, "1982-01-01 00:30:00 +0800", :local, [1982,1,1,0,0,0]) - assert_time_constructor(tz, "1982-01-01 00:59:59 +0800", :local, [1982,1,1,0,29,59]) + assert_time_constructor(tz, "1981-12-31 23:29:59 +0730", :local, [1981,12,31,23,29,59]) + if CORRECT_SINGAPORE_1982 + assert_time_constructor(tz, "1982-01-01 00:00:00 +0800", :local, [1981,12,31,23,30,00]) + assert_time_constructor(tz, "1982-01-01 00:00:00 +0800", :local, [1982,1,1,0,0,0]) + assert_time_constructor(tz, "1982-01-01 00:29:59 +0800", :local, [1982,1,1,0,29,59]) + end assert_time_constructor(tz, "1982-01-01 00:30:00 +0800", :local, [1982,1,1,0,30,0]) } end @@ -196,7 +202,7 @@ class TestTimeTZ < Test::Unit::TestCase def test_europe_lisbon with_tz("Europe/Lisbon") { - assert_equal("LMT", Time.new(-0x1_0000_0000_0000_0000).zone) + assert_include(%w"LMT CET", Time.new(-0x1_0000_0000_0000_0000).zone) } end if has_lisbon_tz @@ -452,9 +458,12 @@ America/Managua Fri Jan 1 06:00:00 1993 UTC = Fri Jan 1 01:00:00 1993 EST isd America/Managua Wed Jan 1 04:59:59 1997 UTC = Tue Dec 31 23:59:59 1996 EST isdst=0 gmtoff=-18000 America/Managua Wed Jan 1 05:00:00 1997 UTC = Tue Dec 31 23:00:00 1996 CST isdst=0 gmtoff=-21600 Asia/Singapore Sun Aug 8 16:30:00 1965 UTC = Mon Aug 9 00:00:00 1965 SGT isdst=0 gmtoff=27000 -Asia/Singapore Thu Dec 31 16:29:59 1981 UTC = Thu Dec 31 23:59:59 1981 SGT isdst=0 gmtoff=27000 +Asia/Singapore Thu Dec 31 15:59:59 1981 UTC = Thu Dec 31 23:29:59 1981 SGT isdst=0 gmtoff=27000 Asia/Singapore Thu Dec 31 16:30:00 1981 UTC = Fri Jan 1 00:30:00 1982 SGT isdst=0 gmtoff=28800 End + gen_zdump_test <<'End' if CORRECT_SINGAPORE_1982 +Asia/Singapore Thu Dec 31 16:00:00 1981 UTC = Fri Jan 1 00:00:00 1982 SGT isdst=0 gmtoff=28800 +End gen_zdump_test CORRECT_TOKYO_DST_1951 ? <<'End' + (CORRECT_TOKYO_DST_1951 < "2018f" ? <<'2018e' : <<'2018f') : <<'End' Asia/Tokyo Sat May 5 14:59:59 1951 UTC = Sat May 5 23:59:59 1951 JST isdst=0 gmtoff=32400 Asia/Tokyo Sat May 5 15:00:00 1951 UTC = Sun May 6 01:00:00 1951 JDT isdst=1 gmtoff=36000 diff --git a/test/rubygems/test_gem_source_git.rb b/test/rubygems/test_gem_source_git.rb index 22fbdee05e..6d7b6b0383 100644 --- a/test/rubygems/test_gem_source_git.rb +++ b/test/rubygems/test_gem_source_git.rb @@ -64,6 +64,11 @@ class TestGemSourceGit < Gem::TestCase end def test_checkout_submodules + # We need to allow to checkout submodules with file:// protocol + # CVE-2022-39253 + # https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/ + system(@git, *%W[config --global protocol.file.allow always]) + source = Gem::Source::Git.new @name, @repository, 'master', true git_gem 'b' diff --git a/test/test_time.rb b/test/test_time.rb index ca20788aac..4f11048596 100644 --- a/test/test_time.rb +++ b/test/test_time.rb @@ -62,6 +62,15 @@ class TestTimeExtension < Test::Unit::TestCase # :nodoc: assert_equal(true, t.utc?) end + def test_rfc2822_nonlinear + pre = ->(n) {"0 Feb 00 00 :00" + " " * n} + assert_linear_performance([100, 500, 5000, 50_000], pre: pre) do |s| + assert_raise(ArgumentError) do + Time.rfc2822(s) + end + end + end + def test_encode_rfc2822 t = Time.utc(1) assert_equal("Mon, 01 Jan 0001 00:00:00 -0000", t.rfc2822) diff --git a/test/uri/test_common.rb b/test/uri/test_common.rb index 1afa35f93d..2b877c10d7 100644 --- a/test/uri/test_common.rb +++ b/test/uri/test_common.rb @@ -56,6 +56,17 @@ class TestCommon < Test::Unit::TestCase assert_raise(NoMethodError) { Object.new.URI("http://www.ruby-lang.org/") } end + def test_parse_timeout + pre = ->(n) { + 'https://example.com/dir/' + 'a' * (n * 100) + '/##.jpg' + } + assert_linear_performance((1..10).map {|i| i * 100}, pre: pre) do |uri| + assert_raise(URI::InvalidURIError) do + URI.parse(uri) + end + end + end + def test_encode_www_form_component assert_equal("%00+%21%22%23%24%25%26%27%28%29*%2B%2C-.%2F09%3A%3B%3C%3D%3E%3F%40" \ "AZ%5B%5C%5D%5E_%60az%7B%7C%7D%7E", diff --git a/test/uri/test_parser.rb b/test/uri/test_parser.rb index b13a26ca84..37e7107bca 100644 --- a/test/uri/test_parser.rb +++ b/test/uri/test_parser.rb @@ -58,4 +58,11 @@ class URI::TestParser < Test::Unit::TestCase assert_equal("\u3042", p1.unescape('%e3%81%82'.force_encoding(Encoding::US_ASCII))) assert_equal("\xe3\x83\x90\xe3\x83\x90", p1.unescape("\xe3\x83\x90%e3%83%90")) end + + def test_split + assert_equal(["http", nil, "example.com", nil, nil, "", nil, nil, nil], URI.split("http://example.com")) + assert_equal(["http", nil, "[0::0]", nil, nil, "", nil, nil, nil], URI.split("http://[0::0]")) + assert_equal([nil, nil, "example.com", nil, nil, "", nil, nil, nil], URI.split("//example.com")) + assert_equal([nil, nil, "[0::0]", nil, nil, "", nil, nil, nil], URI.split("//[0::0]")) + end end diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb index 7d703d15e4..e2cb4e3755 100644 --- a/test/zlib/test_zlib.rb +++ b/test/zlib/test_zlib.rb @@ -3,6 +3,7 @@ require 'test/unit' require 'stringio' require 'tempfile' +require 'securerandom' begin require 'zlib' @@ -443,6 +444,70 @@ if defined? Zlib assert_raise(Zlib::StreamError) { z.set_dictionary("foo") } z.close end + + def test_multithread_deflate + zd = Zlib::Deflate.new + s = "x" * 10000 + (0...10).map do |x| + Thread.new do + 1000.times { zd.deflate(s) } + end + end.each do |th| + th.join + end + ensure + zd&.finish + zd&.close + end + + def test_multithread_inflate + zi = Zlib::Inflate.new + s = Zlib.deflate("x" * 10000) + (0...10).map do |x| + Thread.new do + 1000.times { zi.inflate(s) } + end + end.each do |th| + th.join + end + ensure + zi&.finish + zi&.close + end + + def test_recursive_deflate + original_gc_stress = GC.stress + GC.stress = true + zd = Zlib::Deflate.new + + s = SecureRandom.random_bytes(1024**2) + assert_raise(Zlib::InProgressError) do + zd.deflate(s) do + zd.deflate(s) + end + end + ensure + GC.stress = original_gc_stress + zd&.finish + zd&.close + end + + def test_recursive_inflate + original_gc_stress = GC.stress + GC.stress = true + zi = Zlib::Inflate.new + + s = Zlib.deflate(SecureRandom.random_bytes(1024**2)) + + assert_raise(Zlib::InProgressError) do + zi.inflate(s) do + zi.inflate(s) + end + end + ensure + GC.stress = original_gc_stress + zi&.close + end end class TestZlibGzipFile < Test::Unit::TestCase @@ -741,6 +741,10 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start) } else { errinfo = th->ec->errinfo; + + VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef); + if (!NIL_P(exc)) errinfo = exc; + if (state == TAG_FATAL) { /* fatal error within this thread, need to stop whole script */ } diff --git a/tool/fake.rb b/tool/fake.rb index 42174052e2..4946fd6234 100644 --- a/tool/fake.rb +++ b/tool/fake.rb @@ -43,6 +43,7 @@ prehook = proc do |extmk| $extout_prefix = '$(extout)$(target_prefix)/' config = RbConfig::CONFIG mkconfig = RbConfig::MAKEFILE_CONFIG + $builtruby ||= File.join(builddir, config['RUBY_INSTALL_NAME'] + config['EXEEXT']) RbConfig.fire_update!("builddir", builddir) RbConfig.fire_update!("buildlibdir", builddir) RbConfig.fire_update!("libdir", builddir) diff --git a/tool/gem-unpack.rb b/tool/gem-unpack.rb index 0ddcea0704..08faec9355 100644 --- a/tool/gem-unpack.rb +++ b/tool/gem-unpack.rb @@ -1,3 +1,4 @@ +require 'fileutils' require 'rubygems' require 'rubygems/package' diff --git a/tool/lib/test/unit/core_assertions.rb b/tool/lib/test/unit/core_assertions.rb index 00555d6e32..c25fc4e983 100644 --- a/tool/lib/test/unit/core_assertions.rb +++ b/tool/lib/test/unit/core_assertions.rb @@ -398,6 +398,39 @@ eom end alias all_assertions assert_all_assertions + # Expect +seq+ to respond to +first+ and +each+ methods, e.g., + # Array, Range, Enumerator::ArithmeticSequence and other + # Enumerable-s, and each elements should be size factors. + # + # :yield: each elements of +seq+. + def assert_linear_performance(seq, rehearsal: nil, pre: ->(n) {n}) + first = seq.first + *arg = pre.call(first) + times = (0..(rehearsal || (2 * first))).map do + st = Process.clock_gettime(Process::CLOCK_MONOTONIC) + yield(*arg) + t = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - st) + assert_operator 0, :<=, t + t.nonzero? + end + times.compact! + tmin, tmax = times.minmax + tmax *= tmax / tmin + tmax = 10**Math.log10(tmax).ceil + + seq.each do |i| + next if i == first + t = tmax * i.fdiv(first) + *arg = pre.call(i) + message = "[#{i}]: in #{t}s" + Timeout.timeout(t, Timeout::Error, message) do + st = Process.clock_gettime(Process::CLOCK_MONOTONIC) + yield(*arg) + assert_operator (Process.clock_gettime(Process::CLOCK_MONOTONIC) - st), :<=, t, message + end + end + end + def message(msg = nil, *args, &default) # :nodoc: if Proc === msg super(nil, *args) do @@ -1,12 +1,12 @@ # define RUBY_VERSION_MAJOR RUBY_API_VERSION_MAJOR # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR -#define RUBY_VERSION_TEENY 6 +#define RUBY_VERSION_TEENY 8 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 208 +#define RUBY_PATCHLEVEL 225 -#define RUBY_RELEASE_YEAR 2021 -#define RUBY_RELEASE_MONTH 12 -#define RUBY_RELEASE_DAY 31 +#define RUBY_RELEASE_YEAR 2023 +#define RUBY_RELEASE_MONTH 3 +#define RUBY_RELEASE_DAY 30 #include "ruby/version.h" diff --git a/vm_method.c b/vm_method.c index 3c087841dc..270334ea1b 100644 --- a/vm_method.c +++ b/vm_method.c @@ -584,6 +584,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil if (RTEST(ruby_verbose) && type != VM_METHOD_TYPE_UNDEF && (old_def->alias_count == 0) && + (!old_def->no_redef_warning) && !make_refined && old_def->type != VM_METHOD_TYPE_UNDEF && old_def->type != VM_METHOD_TYPE_ZSUPER && @@ -698,7 +699,13 @@ method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *me, rb_method_visibility_t visi, VALUE defined_class) { rb_method_entry_t *newme = rb_method_entry_make(klass, mid, defined_class, visi, - me->def->type, method_definition_addref(me->def), 0, NULL); + me->def->type, me->def, 0, NULL); + if (newme == me) { + me->def->no_redef_warning = TRUE; + } + else { + method_definition_addref(me->def); + } method_added(klass, mid); return newme; } |