diff --git a/.github/workflows/yjit-macos.yml b/.github/workflows/yjit-macos.yml index 53ffcbe217069e..b44187b0fb1cd8 100644 --- a/.github/workflows/yjit-macos.yml +++ b/.github/workflows/yjit-macos.yml @@ -115,8 +115,10 @@ jobs: ruby -ne 'raise "Disassembly seems broken in dev build (output has too few lines)" unless $_.to_i > 10' if: ${{ contains(matrix.configure, 'jit=dev') }} - - name: Enable YJIT through ENV - run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV + - name: Set ENV for YJIT + run: | + echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV + echo "RUBY_CRASH_REPORT=$(pwd)/rb_crash_%p.txt" >> $GITHUB_ENV - name: Set test options for skipped tests run: | @@ -166,6 +168,10 @@ jobs: if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }} continue-on-error: ${{ matrix.continue-on-skipped_tests || false }} + - if: ${{ failure() }} + continue-on-error: true + run: tail --verbose --lines=+1 rb_crash_*.txt + - uses: ./.github/actions/slack with: label: ${{ matrix.test_task }} ${{ matrix.configure }} ${{ matrix.yjit_opts }} diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index 3ff3310a44a5a0..1fac6e1d9395ae 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -168,8 +168,10 @@ jobs: ruby -ne 'raise "Disassembly seems broken in dev build (output has too few lines)" unless $_.to_i > 10' if: ${{ contains(matrix.configure, 'jit=dev') }} - - name: Enable YJIT through ENV - run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV + - name: Set ENV for YJIT + run: | + echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV + echo "RUBY_CRASH_REPORT=$(pwd)/rb_crash_%p.txt" >> $GITHUB_ENV # Check that the binary was built with YJIT - name: Check YJIT enabled @@ -208,6 +210,11 @@ jobs: LAUNCHABLE_STDERR: ${{ steps.launchable.outputs.stderr_report_path }} continue-on-error: ${{ matrix.continue-on-test_task || false }} + - name: Dump crash logs + if: ${{ failure() }} + continue-on-error: true + run: tail --verbose --lines=+1 rb_crash_*.txt + - uses: ./.github/actions/slack with: label: ${{ matrix.test_task }} ${{ matrix.configure }} diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml index 3da1080be96a09..987a26a62bf6f0 100644 --- a/.github/workflows/zjit-macos.yml +++ b/.github/workflows/zjit-macos.yml @@ -107,6 +107,10 @@ jobs: ruby -ne 'raise "Disassembly seems broken in dev build (output has too few lines)" unless $_.to_i > 10' if: ${{ contains(matrix.configure, 'jit=dev') }} + - name: Set ENV for ZJIT + run: | + echo "RUBY_CRASH_REPORT=$(pwd)/rb_crash_%p.txt" >> $GITHUB_ENV + - name: make ${{ matrix.test_task }} run: >- make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} @@ -122,6 +126,11 @@ jobs: TESTS: ${{ matrix.test_all_opts }} continue-on-error: ${{ matrix.continue-on-test_task || false }} + - name: Dump crash logs + if: ${{ failure() }} + continue-on-error: true + run: tail --verbose --lines=+1 rb_crash_*.txt + - uses: ./.github/actions/slack with: label: ${{ matrix.test_task }} ${{ matrix.configure }} diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml index 14b9eab0423105..c85a3799a006ca 100644 --- a/.github/workflows/zjit-ubuntu.yml +++ b/.github/workflows/zjit-ubuntu.yml @@ -147,6 +147,10 @@ jobs: run: ./miniruby --zjit -v | grep "+ZJIT" if: ${{ matrix.configure != '--disable-zjit' }} + - name: Set ENV for ZJIT + run: | + echo "RUBY_CRASH_REPORT=$(pwd)/rb_crash_%p.txt" >> $GITHUB_ENV + - name: make ${{ matrix.test_task }} run: >- make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} @@ -164,6 +168,11 @@ jobs: TESTS: ${{ matrix.test_all_opts }} continue-on-error: ${{ matrix.continue-on-test_task || false }} + - name: Dump crash logs + if: ${{ failure() }} + continue-on-error: true + run: tail --verbose --lines=+1 rb_crash_*.txt + - uses: ./.github/actions/slack with: label: ${{ matrix.test_task }} ${{ matrix.configure }} diff --git a/array.c b/array.c index d9dff28ad95ebd..2ba95bef186dae 100644 --- a/array.c +++ b/array.c @@ -695,6 +695,11 @@ ary_alloc_heap(VALUE klass) NEWOBJ_OF(ary, struct RArray, klass, T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0), sizeof(struct RArray), 0); + + ary->as.heap.len = 0; + ary->as.heap.aux.capa = 0; + ary->as.heap.ptr = NULL; + return (VALUE)ary; } @@ -808,6 +813,11 @@ ec_ary_alloc_heap(rb_execution_context_t *ec, VALUE klass) NEWOBJ_OF(ary, struct RArray, klass, T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0), sizeof(struct RArray), ec); + + ary->as.heap.len = 0; + ary->as.heap.aux.capa = 0; + ary->as.heap.ptr = NULL; + return (VALUE)ary; } diff --git a/bignum.c b/bignum.c index 2e135caf20fb09..054b6c1cc9afe3 100644 --- a/bignum.c +++ b/bignum.c @@ -3055,7 +3055,9 @@ bignew_1(VALUE klass, size_t len, int sign) VALUE rb_big_new(size_t len, int sign) { - return bignew(len, sign != 0); + VALUE obj = bignew(len, sign != 0); + memset(BIGNUM_DIGITS(obj), 0, len * sizeof(BDIGIT)); + return obj; } VALUE diff --git a/class.c b/class.c index 472095617c1e8a..b15f9f5ae7258d 100644 --- a/class.c +++ b/class.c @@ -682,6 +682,8 @@ class_alloc0(enum ruby_value_type type, VALUE klass, bool namespaceable) NEWOBJ_OF(obj, struct RClass, klass, flags, alloc_size, 0); + obj->object_id = 0; + memset(RCLASS_EXT_PRIME(obj), 0, sizeof(rb_classext_t)); /* ZALLOC diff --git a/configure.ac b/configure.ac index 6d91d764456e36..b035e1688a8461 100644 --- a/configure.ac +++ b/configure.ac @@ -113,9 +113,10 @@ AC_ARG_WITH(git, { test x"$HAVE_GIT" = xyes && command -v "$GIT" > /dev/null && - # see Dependencies in doc/contributing/building_ruby.md + # `git -C`: 1.8.5 + # `git log --no-show-signature`: 2.10.0 AS_CASE([`$GIT -C . --version 2> /dev/null | sed 's/.* //'`], - [0.*|1.*|2.@<:@0-9@:>@.*|2.@<:@12@:>@@<:@0-9@:>@.*|2.3@<:@01@:>@.*], [false], + [0.*|1.*|2.@<:@0-9@:>@.*], [false], [true]) } || HAVE_GIT=no GIT=never-use AC_SUBST(GIT) diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index 07e651335d53e1..bec634ae11a1f0 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -32,6 +32,7 @@ */ static VALUE cCipher; static VALUE eCipherError; +static VALUE eAuthTagError; static ID id_auth_tag_len, id_key_set; static VALUE ossl_cipher_alloc(VALUE klass); @@ -415,8 +416,17 @@ ossl_cipher_final(VALUE self) GetCipher(self, ctx); str = rb_str_new(0, EVP_CIPHER_CTX_block_size(ctx)); - if (!EVP_CipherFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), &out_len)) - ossl_raise(eCipherError, NULL); + if (!EVP_CipherFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), &out_len)) { + /* For AEAD ciphers, this is likely an authentication failure */ + if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) { + /* For AEAD ciphers, EVP_CipherFinal_ex failures are authentication tag verification failures */ + ossl_raise(eAuthTagError, "AEAD authentication tag verification failed"); + } + else { + /* For non-AEAD ciphers */ + ossl_raise(eCipherError, "cipher final failed"); + } + } assert(out_len <= RSTRING_LEN(str)); rb_str_set_len(str, out_len); @@ -1027,6 +1037,7 @@ Init_ossl_cipher(void) */ cCipher = rb_define_class_under(mOSSL, "Cipher", rb_cObject); eCipherError = rb_define_class_under(cCipher, "CipherError", eOSSLError); + eAuthTagError = rb_define_class_under(cCipher, "AuthTagError", eCipherError); rb_define_alloc_func(cCipher, ossl_cipher_alloc); rb_define_method(cCipher, "initialize_copy", ossl_cipher_copy, 1); diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index ee6a7a988f59f0..0fed03332fc31e 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -83,15 +83,13 @@ ossl_pkey_wrap(EVP_PKEY *pkey) # include static EVP_PKEY * -ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass, - int *retryable) +ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass) { void *ppass = (void *)pass; OSSL_DECODER_CTX *dctx; EVP_PKEY *pkey = NULL; int pos = 0, pos2; - *retryable = 0; dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, input_type, NULL, NULL, selection, NULL, NULL); if (!dctx) @@ -102,25 +100,17 @@ ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass, goto out; while (1) { if (OSSL_DECODER_from_bio(dctx, bio) == 1) + goto out; + if (BIO_eof(bio)) break; - // Error queue may not be populated in OpenSSL < 3.0.11 and < 3.1.3 - // https://github.com/openssl/openssl/pull/21603 - unsigned long err = ERR_peek_error(); - if (err && ERR_GET_REASON(err) != ERR_R_UNSUPPORTED) - break; - if (BIO_eof(bio) == 1) { - *retryable = 1; - break; - } pos2 = BIO_tell(bio); - if (pos2 < 0 || pos2 <= pos) { - *retryable = 1; + if (pos2 < 0 || pos2 <= pos) break; - } ossl_clear_error(); pos = pos2; } out: + OSSL_BIO_reset(bio); OSSL_DECODER_CTX_free(dctx); return pkey; } @@ -128,6 +118,7 @@ ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass, EVP_PKEY * ossl_pkey_read_generic(BIO *bio, VALUE pass) { + EVP_PKEY *pkey = NULL; /* First check DER, then check PEM. */ const char *input_types[] = {"DER", "PEM"}; int input_type_num = (int)(sizeof(input_types) / sizeof(char *)); @@ -176,22 +167,18 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass) EVP_PKEY_PUBLIC_KEY }; int selection_num = (int)(sizeof(selections) / sizeof(int)); + int i, j; - for (int i = 0; i < input_type_num; i++) { - for (int j = 0; j < selection_num; j++) { - if (i || j) { - ossl_clear_error(); - BIO_reset(bio); + for (i = 0; i < input_type_num; i++) { + for (j = 0; j < selection_num; j++) { + pkey = ossl_pkey_read(bio, input_types[i], selections[j], pass); + if (pkey) { + goto out; } - - int retryable; - EVP_PKEY *pkey = ossl_pkey_read(bio, input_types[i], selections[j], - pass, &retryable); - if (pkey || !retryable) - return pkey; } } - return NULL; + out: + return pkey; } #else EVP_PKEY * diff --git a/gc.c b/gc.c index 0433da2e67c282..5a18afa4a71417 100644 --- a/gc.c +++ b/gc.c @@ -620,7 +620,7 @@ typedef struct gc_function_map { void (*stress_set)(void *objspace_ptr, VALUE flag); VALUE (*stress_get)(void *objspace_ptr); // Object allocation - VALUE (*new_obj)(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, bool wb_protected, size_t alloc_size); + VALUE (*new_obj)(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size); size_t (*obj_slot_size)(VALUE obj); size_t (*heap_id_for_size)(void *objspace_ptr, size_t size); bool (*size_allocatable_p)(size_t size); @@ -984,9 +984,9 @@ gc_validate_pc(void) } static inline VALUE -newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, bool wb_protected, size_t size) +newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, bool wb_protected, size_t size) { - VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, v1, v2, v3, wb_protected, size); + VALUE obj = rb_gc_impl_new_obj(rb_gc_get_objspace(), cr->newobj_cache, klass, flags, wb_protected, size); gc_validate_pc(); @@ -1016,9 +1016,9 @@ newobj_of(rb_ractor_t *cr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v # endif memset( - (void *)(obj + RVALUE_SIZE), + (void *)(obj + sizeof(struct RBasic)), GC_DEBUG_SLOT_FILL_SPECIAL_VALUE, - rb_gc_obj_slot_size(obj) - RVALUE_SIZE + rb_gc_obj_slot_size(obj) - sizeof(struct RBasic) ); #endif @@ -1029,14 +1029,14 @@ VALUE rb_wb_unprotected_newobj_of(VALUE klass, VALUE flags, size_t size) { GC_ASSERT((flags & FL_WB_PROTECTED) == 0); - return newobj_of(GET_RACTOR(), klass, flags, 0, 0, 0, FALSE, size); + return newobj_of(GET_RACTOR(), klass, flags, FALSE, size); } VALUE rb_wb_protected_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flags, size_t size) { GC_ASSERT((flags & FL_WB_PROTECTED) == 0); - return newobj_of(rb_ec_ractor_ptr(ec), klass, flags, 0, 0, 0, TRUE, size); + return newobj_of(rb_ec_ractor_ptr(ec), klass, flags, TRUE, size); } #define UNEXPECTED_NODE(func) \ @@ -1057,7 +1057,14 @@ rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FU { RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1); if (klass) rb_data_object_check(klass); - return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap, !dmark, sizeof(struct RTypedData)); + VALUE obj = newobj_of(GET_RACTOR(), klass, T_DATA, !dmark, sizeof(struct RTypedData)); + + struct RData *data = (struct RData *)obj; + data->dmark = dmark; + data->dfree = dfree; + data->data = datap; + + return obj; } VALUE @@ -1074,7 +1081,14 @@ typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_ RBIMPL_NONNULL_ARG(type); if (klass) rb_data_object_check(klass); bool wb_protected = (type->flags & RUBY_FL_WB_PROTECTED) || !type->function.dmark; - return newobj_of(GET_RACTOR(), klass, T_DATA | RUBY_TYPED_FL_IS_TYPED_DATA, 0, ((VALUE)type) | typed_flag, (VALUE)datap, wb_protected, size); + VALUE obj = newobj_of(GET_RACTOR(), klass, T_DATA | RUBY_TYPED_FL_IS_TYPED_DATA, wb_protected, size); + + struct RTypedData *data = (struct RTypedData *)obj; + data->fields_obj = 0; + *(VALUE *)&data->type = ((VALUE)type) | typed_flag; + data->data = datap; + + return obj; } VALUE diff --git a/gc/default/default.c b/gc/default/default.c index 7264fb799627eb..b7d37149734c40 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -2096,16 +2096,6 @@ heap_prepare(rb_objspace_t *objspace, rb_heap_t *heap) GC_ASSERT(heap->free_pages != NULL); } -static inline VALUE -newobj_fill(VALUE obj, VALUE v1, VALUE v2, VALUE v3) -{ - VALUE *p = (VALUE *)(obj + sizeof(struct RBasic)); - p[0] = v1; - p[1] = v2; - p[2] = v3; - return obj; -} - #if GC_DEBUG static inline const char* rb_gc_impl_source_location_cstr(int *ptr) @@ -2146,8 +2136,6 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace, #endif #if RGENGC_CHECK_MODE - newobj_fill(obj, 0, 0, 0); - int lev = RB_GC_VM_LOCK_NO_BARRIER(); { check_rvalue_consistency(objspace, obj); @@ -2469,7 +2457,7 @@ newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace } VALUE -rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, bool wb_protected, size_t alloc_size) +rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size) { VALUE obj; rb_objspace_t *objspace = objspace_ptr; @@ -2500,7 +2488,7 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags newobj_slowpath_wb_unprotected(klass, flags, objspace, cache, heap_idx); } - return newobj_fill(obj, v1, v2, v3); + return obj; } static int diff --git a/gc/gc_impl.h b/gc/gc_impl.h index b461f3a3054cd2..3250483639e775 100644 --- a/gc/gc_impl.h +++ b/gc/gc_impl.h @@ -55,7 +55,7 @@ GC_IMPL_FN VALUE rb_gc_impl_stress_get(void *objspace_ptr); GC_IMPL_FN VALUE rb_gc_impl_config_get(void *objspace_ptr); GC_IMPL_FN void rb_gc_impl_config_set(void *objspace_ptr, VALUE hash); // Object allocation -GC_IMPL_FN VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, bool wb_protected, size_t alloc_size); +GC_IMPL_FN VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size); GC_IMPL_FN size_t rb_gc_impl_obj_slot_size(VALUE obj); GC_IMPL_FN size_t rb_gc_impl_heap_id_for_size(void *objspace_ptr, size_t size); GC_IMPL_FN bool rb_gc_impl_size_allocatable_p(size_t size); diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index 7b7f76eb1a564e..9dd3129e01664e 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -598,7 +598,7 @@ rb_gc_impl_config_set(void *objspace_ptr, VALUE hash) // Object allocation VALUE -rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, bool wb_protected, size_t alloc_size) +rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size) { #define MMTK_ALLOCATION_SEMANTICS_DEFAULT 0 struct objspace *objspace = objspace_ptr; @@ -622,9 +622,6 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags alloc_obj[-1] = alloc_size; alloc_obj[0] = flags; alloc_obj[1] = klass; - if (alloc_size > 16) alloc_obj[2] = v1; - if (alloc_size > 24) alloc_obj[3] = v2; - if (alloc_size > 32) alloc_obj[4] = v3; mmtk_post_alloc(ractor_cache->mutator, (void*)alloc_obj, alloc_size + 8, MMTK_ALLOCATION_SEMANTICS_DEFAULT); diff --git a/imemo.c b/imemo.c index 74eae5678f3532..10ac960095675f 100644 --- a/imemo.c +++ b/imemo.c @@ -54,6 +54,9 @@ rb_imemo_tmpbuf_new(void) VALUE flags = T_IMEMO | (imemo_tmpbuf << FL_USHIFT); NEWOBJ_OF(obj, rb_imemo_tmpbuf_t, 0, flags, sizeof(rb_imemo_tmpbuf_t), NULL); + obj->ptr = NULL; + obj->cnt = 0; + return (VALUE)obj; } diff --git a/iseq.h b/iseq.h index 1cecc6960d0b5a..c7f091a0b4c2df 100644 --- a/iseq.h +++ b/iseq.h @@ -175,7 +175,12 @@ ISEQ_COMPILE_DATA_CLEAR(rb_iseq_t *iseq) static inline rb_iseq_t * iseq_imemo_alloc(void) { - return IMEMO_NEW(rb_iseq_t, imemo_iseq, 0); + rb_iseq_t *iseq = IMEMO_NEW(rb_iseq_t, imemo_iseq, 0); + + // Clear out the whole iseq except for the flags. + memset((char *)iseq + sizeof(VALUE), 0, sizeof(rb_iseq_t) - sizeof(VALUE)); + + return iseq; } VALUE rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt); diff --git a/lib/erb.rb b/lib/erb.rb index bb2baf089f739b..9c4cac3f10c988 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -531,6 +531,49 @@ # # => # # ``` # +# ## Error Reporting +# +# Consider this template (containing an error): +# +# ``` +# s = '<%= nosuch %>' +# template = ERB.new(s) +# ``` +# +# When \ERB reports an error, +# it includes a file name (if available) and a line number; +# the file name comes from method #filename, the line number from method #lineno. +# +# Initially, those values are `nil` and `0`, respectively; +# these initial values are reported as `'(erb)'` and `1`, respectively: +# +# ``` +# template.filename # => nil +# template.lineno # => 0 +# template.result +# (erb):1:in '
': undefined local variable or method 'nosuch' for main (NameError) +# ``` +# +# You can use methods #filename= and #lineno= to assign values +# that are more meaningful in your context: +# +# ``` +# template.filename = 't.txt' +# # => "t.txt" +# template.lineno = 555 +# # => 555 +# template.result +# t.txt:556:in '
': undefined local variable or method 'nosuch' for main (NameError) +# ``` +# +# You can use method #location= to set both values: +# +# ``` +# template.location = ['u.txt', 999] +# template.result +# u.txt:1000:in '
': undefined local variable or method 'nosuch' for main (NameError) +# ``` +# # ## Plain Text Example # # Here's a plain-text string; @@ -833,29 +876,32 @@ def make_compiler(trim_mode) # The encoding to eval attr_reader :encoding - # The optional _filename_ argument passed to Kernel#eval when the ERB code - # is run + # :markup: markdown + # + # Sets or returns the file name to be used in reporting errors; + # see [Error Reporting][error reporting]. + # + # [error reporting]: rdoc-ref:ERB@Error+Reporting attr_accessor :filename - # The optional _lineno_ argument passed to Kernel#eval when the ERB code - # is run + # :markup: markdown + # + # Sets or returns the line number to be used in reporting errors; + # see [Error Reporting][error reporting]. + # + # [error reporting]: rdoc-ref:ERB@Error+Reporting attr_accessor :lineno + # :markup: markdown # - # Sets optional filename and line number that will be used in ERB code - # evaluation and error reporting. See also #filename= and #lineno= - # - # erb = ERB.new('<%= some_x %>') - # erb.render - # # undefined local variable or method `some_x' - # # from (erb):1 + # :call-seq: + # location = [filename, lineno] => [filename, lineno] + # location = filename -> filename # - # erb.location = ['file.erb', 3] - # # All subsequent error reporting would use new location - # erb.render - # # undefined local variable or method `some_x' - # # from file.erb:4 + # Sets the values of #filename and, if given, #lineno; + # see [Error Reporting][error reporting]. # + # [error reporting]: rdoc-ref:ERB@Error+Reporting def location=((filename, lineno)) @filename = filename @lineno = lineno if lineno diff --git a/prism/prism.c b/prism/prism.c index 2e202c37456ea5..42a821e53584a9 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -20909,7 +20909,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding bool permitted = true; if (previous_binding_power != PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_USTAR)) permitted = false; - pm_node_t *value = parse_starred_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH, diag_id, (uint16_t) (depth + 1)); + pm_node_t *value = parse_starred_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MODIFIER, diag_id, (uint16_t) (depth + 1)); if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE); parse_assignment_value_local(parser, value); diff --git a/sample/openssl/c_rehash.rb b/sample/openssl/c_rehash.rb index de4b66e9022b88..8b005bbb849ac3 100644 --- a/sample/openssl/c_rehash.rb +++ b/sample/openssl/c_rehash.rb @@ -156,7 +156,7 @@ def path(filename) end def hash_name(name) - sprintf("%x", name.hash) + sprintf("%08x", name.hash) end def fingerprint(der) diff --git a/spec/bundled_gems_spec.rb b/spec/bundled_gems_spec.rb index c362881850f775..8014a48223c8cf 100644 --- a/spec/bundled_gems_spec.rb +++ b/spec/bundled_gems_spec.rb @@ -31,7 +31,7 @@ def self.ruby=(ruby) FileUtils.cp_r Spec::Path.pristine_system_gem_path, Spec::Path.system_gem_path FileUtils.mkdir_p Spec::Path.base_system_gem_path.join("gems") %w[sinatra rack tilt rack-protection rack-session rack-test mustermann base64 logger compact_index].each do |gem| - path = Dir[File.expand_path("../.bundle/gems/#{gem}-*", __dir__)].map(&:to_s).first + path, = Dir[File.expand_path("../.bundle/gems/#{gem}-*", __dir__)] FileUtils.cp_r path, Spec::Path.base_system_gem_path.join("gems") end diff --git a/string.c b/string.c index 834641cfdbf0c1..624c61896f01f7 100644 --- a/string.c +++ b/string.c @@ -1006,6 +1006,9 @@ str_alloc_embed(VALUE klass, size_t capa) NEWOBJ_OF(str, struct RString, klass, T_STRING | (RGENGC_WB_PROTECTED_STRING ? FL_WB_PROTECTED : 0), size, 0); + str->len = 0; + str->as.embed.ary[0] = 0; + return (VALUE)str; } @@ -1015,6 +1018,10 @@ str_alloc_heap(VALUE klass) NEWOBJ_OF(str, struct RString, klass, T_STRING | STR_NOEMBED | (RGENGC_WB_PROTECTED_STRING ? FL_WB_PROTECTED : 0), sizeof(struct RString), 0); + str->len = 0; + str->as.heap.aux.capa = 0; + str->as.heap.ptr = NULL; + return (VALUE)str; } @@ -1866,6 +1873,8 @@ ec_str_alloc_embed(struct rb_execution_context_struct *ec, VALUE klass, size_t c NEWOBJ_OF(str, struct RString, klass, T_STRING | (RGENGC_WB_PROTECTED_STRING ? FL_WB_PROTECTED : 0), size, ec); + str->len = 0; + return (VALUE)str; } @@ -1875,6 +1884,9 @@ ec_str_alloc_heap(struct rb_execution_context_struct *ec, VALUE klass) NEWOBJ_OF(str, struct RString, klass, T_STRING | STR_NOEMBED | (RGENGC_WB_PROTECTED_STRING ? FL_WB_PROTECTED : 0), sizeof(struct RString), ec); + str->as.heap.aux.capa = 0; + str->as.heap.ptr = NULL; + return (VALUE)str; } diff --git a/struct.c b/struct.c index c53e68b3da7f67..2076f5709e304d 100644 --- a/struct.c +++ b/struct.c @@ -837,6 +837,7 @@ struct_alloc(VALUE klass) st->as.heap.ptr = struct_heap_alloc((VALUE)st, n); rb_mem_clear((VALUE *)st->as.heap.ptr, n); st->as.heap.len = n; + st->as.heap.fields_obj = 0; return (VALUE)st; } diff --git a/test/-ext-/bug_reporter/test_bug_reporter.rb b/test/-ext-/bug_reporter/test_bug_reporter.rb index 1350675a48d3ed..83fdba22829e97 100644 --- a/test/-ext-/bug_reporter/test_bug_reporter.rb +++ b/test/-ext-/bug_reporter/test_bug_reporter.rb @@ -23,7 +23,7 @@ def test_bug_reporter_add # We want the printed description to match this process's RUBY_DESCRIPTION args.push("--yjit") if JITSupport.yjit_enabled? args.push("--zjit") if JITSupport.zjit_enabled? - args.unshift({"RUBY_ON_BUG" => nil}) + args.unshift({"RUBY_ON_BUG" => nil, "RUBY_CRASH_REPORT" => nil}) stdin = "#{no_core}register_sample_bug_reporter(12345); Process.kill :SEGV, $$" assert_in_out_err(args, stdin, [], expected_stderr, encoding: "ASCII-8BIT") ensure diff --git a/test/objspace/test_ractor.rb b/test/objspace/test_ractor.rb index eb3044cda3c3c3..a14604c1e7c05f 100644 --- a/test/objspace/test_ractor.rb +++ b/test/objspace/test_ractor.rb @@ -2,6 +2,10 @@ class TestObjSpaceRactor < Test::Unit::TestCase def test_tracing_does_not_crash + # https://ci.rvm.jp/results/trunk-random1@ruby-sp2-noble-docker/5954509 + # https://ci.rvm.jp/results/trunk-random0@ruby-sp2-noble-docker/5954501 + omit "crashes frequently on CI but not able to reproduce locally" + assert_ractor(<<~RUBY, require: 'objspace') ObjectSpace.trace_object_allocations do r = Ractor.new do diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb index cd0b3dcb4450f6..10858b353559e1 100644 --- a/test/openssl/test_cipher.rb +++ b/test/openssl/test_cipher.rb @@ -182,6 +182,10 @@ def test_update_raise_if_key_not_set end end + def test_auth_tag_error_inheritance + assert_equal OpenSSL::Cipher::CipherError, OpenSSL::Cipher::AuthTagError.superclass + end + def test_authenticated cipher = OpenSSL::Cipher.new('aes-128-gcm') assert_predicate(cipher, :authenticated?) @@ -212,7 +216,8 @@ def test_aes_ccm cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag[0, 8], auth_data: aad) assert_equal pt, cipher.update(ct) << cipher.final - # wrong tag is rejected + # wrong tag is rejected - in CCM, authentication happens during update, but + # we consider this a general CipherError since update failures can have various causes tag2 = tag.dup tag2.setbyte(-1, (tag2.getbyte(-1) + 1) & 0xff) cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag2, auth_data: aad) @@ -265,19 +270,19 @@ def test_aes_gcm tag2.setbyte(-1, (tag2.getbyte(-1) + 1) & 0xff) cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag2, auth_data: aad) cipher.update(ct) - assert_raise(OpenSSL::Cipher::CipherError) { cipher.final } + assert_raise(OpenSSL::Cipher::AuthTagError) { cipher.final } # wrong aad is rejected aad2 = aad[0..-2] << aad[-1].succ cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad2) cipher.update(ct) - assert_raise(OpenSSL::Cipher::CipherError) { cipher.final } + assert_raise(OpenSSL::Cipher::AuthTagError) { cipher.final } # wrong ciphertext is rejected ct2 = ct[0..-2] << ct[-1].succ cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad) cipher.update(ct2) - assert_raise(OpenSSL::Cipher::CipherError) { cipher.final } + assert_raise(OpenSSL::Cipher::AuthTagError) { cipher.final } end def test_aes_gcm_variable_iv_len diff --git a/test/openssl/test_pkey.rb b/test/openssl/test_pkey.rb index 24a595333b4ea3..8066c4dc190fa9 100644 --- a/test/openssl/test_pkey.rb +++ b/test/openssl/test_pkey.rb @@ -124,17 +124,6 @@ def test_s_read_passphrase } } assert_equal(1, called) - - # Incorrect passphrase returned by the block. The input contains two PEM - # blocks. - called = 0 - assert_raise(OpenSSL::PKey::PKeyError) { - OpenSSL::PKey.read(encrypted_pem + encrypted_pem) { - called += 1 - "incorrect_passphrase" - } - } - assert_equal(1, called) end def test_s_read_passphrase_tty diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index fc19f4d9459d86..7abe2c6df5d330 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -685,6 +685,8 @@ def test_sslctx_set_params end def test_post_connect_check_with_anon_ciphers + # DH missing the q value on unknown named parameters is not FIPS-approved. + omit_on_fips omit "AWS-LC does not support DHE ciphersuites" if aws_lc? ctx_proc = -> ctx { @@ -1747,6 +1749,9 @@ def test_sync_close_without_connect end def test_get_ephemeral_key + # kRSA is not FIPS-approved. + omit_on_fips + # kRSA ctx_proc1 = proc { |ctx| ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION @@ -1863,6 +1868,8 @@ def test_fallback_scsv end def test_tmp_dh_callback + # DH missing the q value on unknown named parameters is not FIPS-approved. + omit_on_fips omit "AWS-LC does not support DHE ciphersuites" if aws_lc? dh = Fixtures.pkey("dh-1") @@ -2131,6 +2138,8 @@ def test_connect_works_when_setting_dh_callback_to_nil end def test_tmp_dh + # DH missing the q value on unknown named parameters is not FIPS-approved. + omit_on_fips omit "AWS-LC does not support DHE ciphersuites" if aws_lc? dh = Fixtures.pkey("dh-1") diff --git a/test/prism/errors/command_calls_33.txt b/test/prism/errors/command_calls_33.txt new file mode 100644 index 00000000000000..13e3b35c9e83b6 --- /dev/null +++ b/test/prism/errors/command_calls_33.txt @@ -0,0 +1,6 @@ +1 if foo = bar baz + ^~~ unexpected local variable or method, expecting end-of-input + +1 and foo = bar baz + ^~~ unexpected local variable or method, expecting end-of-input + diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index 2c9f8534b19aad..2b7cf1c8999a4c 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -855,6 +855,7 @@ def assert_segv(args, message=nil, list: SEGVTest::ExpectedStderrList, **opt, &b args.unshift("--yjit") if JITSupport.yjit_enabled? args.unshift("--zjit") if JITSupport.zjit_enabled? env.update({'RUBY_ON_BUG' => nil}) + env['RUBY_CRASH_REPORT'] ||= nil # default to not passing down parent setting # ASAN registers a segv handler which prints out "AddressSanitizer: DEADLYSIGNAL" when # catching sigsegv; we don't expect that output, so suppress it. env.update({'ASAN_OPTIONS' => 'handle_segv=0'}) diff --git a/test/ruby/test_vm_dump.rb b/test/ruby/test_vm_dump.rb index a3e7b69913b7d2..d183e033915cd7 100644 --- a/test/ruby/test_vm_dump.rb +++ b/test/ruby/test_vm_dump.rb @@ -5,6 +5,7 @@ class TestVMDump < Test::Unit::TestCase def assert_darwin_vm_dump_works(args, timeout=nil) + args.unshift({"RUBY_ON_BUG" => nil, "RUBY_CRASH_REPORT" => nil}) assert_in_out_err(args, "", [], /^\[IMPORTANT\]/, timeout: timeout || 300) end @@ -13,7 +14,7 @@ def test_darwin_invalid_call end def test_darwin_segv_in_syscall - assert_darwin_vm_dump_works('-e1.times{Process.kill :SEGV,$$}') + assert_darwin_vm_dump_works(['-e1.times{Process.kill :SEGV,$$}']) end def test_darwin_invalid_access diff --git a/tool/fetch-bundled_gems.rb b/tool/fetch-bundled_gems.rb index e46c5bdc1c5ff0..127ea236f3d5e4 100755 --- a/tool/fetch-bundled_gems.rb +++ b/tool/fetch-bundled_gems.rb @@ -40,9 +40,8 @@ end checkout = %w"git -c advice.detachedHead=false checkout" -print %[checking out #{color.notice(c)} (v=#{color.info(v)}] -print %[, r=#{color.info(r)}] if r -puts ") ..." +info = %[, r=#{color.info(r)}] if r +puts "checking out #{color.notice(c)} (v=#{color.info(v)}#{info}) ..." unless system(*checkout, c, "--", chdir: n) abort if r or !system(*checkout, v, "--", chdir: n) end diff --git a/tool/lib/vcs.rb b/tool/lib/vcs.rb index 2c019d81fd421f..45b84b303503ad 100644 --- a/tool/lib/vcs.rb +++ b/tool/lib/vcs.rb @@ -51,23 +51,9 @@ def popen(*args) end using DebugPOpen module DebugSystem - def system(*args) + def system(*args, exception: true, **opts) VCS.dump(args, "args: ") if $DEBUG - exception = false - opts = Hash.try_convert(args[-1]) - if RUBY_VERSION >= "2.6" - unless opts - opts = {} - args << opts - end - exception = opts.fetch(:exception) {opts[:exception] = true} - elsif opts - exception = opts.delete(:exception) {true} - args.pop if opts.empty? - end - ret = super(*args) - raise "Command failed with status (#$?): #{args[0]}" if exception and !ret - ret + super(*args, exception: exception, **opts) end end diff --git a/tool/test-bundled-gems.rb b/tool/test-bundled-gems.rb index c44b78d63b4bdf..e73a29c54bb6ae 100644 --- a/tool/test-bundled-gems.rb +++ b/tool/test-bundled-gems.rb @@ -19,7 +19,7 @@ allowed_failures = allowed_failures.split(',').concat(DEFAULT_ALLOWED_FAILURES).uniq.reject(&:empty?) # make test-bundled-gems BUNDLED_GEMS=gem1,gem2,gem3 -bundled_gems = ARGV.first || '' +bundled_gems = nil if (bundled_gems = ARGV.first&.split(","))&.empty? colorize = Colorize.new rake = File.realpath("../../.bundle/bin/rake", __FILE__) @@ -30,7 +30,7 @@ failed = [] File.foreach("#{gem_dir}/bundled_gems") do |line| next unless gem = line[/^[^\s\#]+/] - next unless bundled_gems.empty? || bundled_gems.split(",").include?(gem) + next if bundled_gems&.none? {|pat| File.fnmatch?(pat, gem)} next unless File.directory?("#{gem_dir}/src/#{gem}/test") test_command = "#{ruby} -C #{gem_dir}/src/#{gem} #{rake} test" diff --git a/tool/test/testunit/test_parallel.rb b/tool/test/testunit/test_parallel.rb index d87e0ed3271a35..adf7d62ecd581a 100644 --- a/tool/test/testunit/test_parallel.rb +++ b/tool/test/testunit/test_parallel.rb @@ -151,9 +151,9 @@ def test_quit end class TestParallel < Test::Unit::TestCase - def spawn_runner(*opt_args, jobs: "t1") + def spawn_runner(*opt_args, jobs: "t1", env: {}) @test_out, o = IO.pipe - @test_pid = spawn(*@__runner_options__[:ruby], TESTS+"/runner.rb", + @test_pid = spawn(env, *@__runner_options__[:ruby], TESTS+"/runner.rb", "--ruby", @__runner_options__[:ruby].join(" "), "-j", jobs, *opt_args, out: o, err: o) o.close @@ -214,7 +214,7 @@ def test_separate end def test_hungup - spawn_runner "--worker-timeout=1", "--retry", "test4test_hungup.rb" + spawn_runner("--worker-timeout=1", "--retry", "test4test_hungup.rb", env: {"RUBY_CRASH_REPORT"=>nil}) buf = ::TestParallel.timeout(TIMEOUT) {@test_out.read} assert_match(/^Retrying hung up testcases\.+$/, buf) assert_match(/^2 tests,.* 0 failures,/, buf) diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 917c407a006124..81a33277afb161 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -526,7 +526,7 @@ HAVE_GIT = no !else if [for /f "tokens=3" %I in ('git --version') do @(\ for /f "delims=. tokens=1-2" %I in ("%I") do @(\ if %I lss 2 (exit 1) else if %I gtr 2 (exit 0) \ - else if %J lss 32 (exit 1) else (exit 0)\ + else if %J lss 10 (exit 1) else (exit 0)\ )\ )] HAVE_GIT = no diff --git a/zjit/src/profile.rs b/zjit/src/profile.rs index c53943a4577abb..f73138d4bdcc41 100644 --- a/zjit/src/profile.rs +++ b/zjit/src/profile.rs @@ -150,7 +150,8 @@ impl Flags { /// opt_send_without_block/opt_plus/... should store: /// * the class of the receiver, so we can do method lookup /// * the shape of the receiver, so we can optimize ivar lookup -/// with those two, pieces of information, we can also determine when an object is an immediate: +/// +/// with those two, pieces of information, we can also determine when an object is an immediate: /// * Integer + IS_IMMEDIATE == Fixnum /// * Float + IS_IMMEDIATE == Flonum /// * Symbol + IS_IMMEDIATE == StaticSymbol