diff --git a/SOURCES/ruby-3.2.0-Detect-compaction-support-during-runtime.patch b/SOURCES/ruby-3.2.0-Detect-compaction-support-during-runtime.patch new file mode 100644 index 0000000..3b7b3da --- /dev/null +++ b/SOURCES/ruby-3.2.0-Detect-compaction-support-during-runtime.patch @@ -0,0 +1,283 @@ +From ec992161c3ca346061fb5b1b9c44402a03105b79 Mon Sep 17 00:00:00 2001 +From: Jarek Prokop +Date: Wed, 22 Jun 2022 01:03:49 +0200 +Subject: [PATCH] Detect compaction support during runtime. + +The patch is created by backporting 3 commits from +the upstream Ruby master branch in the chronological order below. + +https://github.com/ruby/ruby/commit/52d42e702375446746164a0251e1a10bce813b78 +https://github.com/ruby/ruby/commit/79eaaf2d0b641710613f16525e4b4c439dfe854e +https://github.com/ruby/ruby/commit/2c190863239bee3f54cfb74b16bb6ea4cae6ed20 + +== How to create this patch == + +Download Ruby source code. +``` +$ git clone https://github.com/ruby/ruby.git +$ cd ruby +``` + +First create a commit squashed from the 3 commits above. +Checkout the second commmit above, and create a temporary branch. +``` +$ git checkout 79eaaf2d0b641710613f16525e4b4c439dfe854e +$ git checkout -b wip/detect-compaction-runtime-tmp +``` + +Cherry pick the third commit on the second commit. +``` +$ git cherry-pick 2c190863239bee3f54cfb74b16bb6ea4cae6ed20 +``` + +Squash the last 3 commits on the branch. +``` +$ git rebase -i 2223eb082afa6d05321b69df783d4133b9aacba6 +``` + +Then checkout Ruby 3.1.2 branch. +Create a new branch. +Merge the Fedora Ruby's +ruby-3.2.0-define-unsupported-gc-compaction-methods-as-rb_f_notimplement.patch. +``` +$ git checkout v3_1_2 +$ git checkout -b wip/detect-compaction-runtime +$ patch -p1 < +~/fed/ruby/ruby-3.2.0-define-unsupported-gc-compaction-methods-as-rb_f_notimplement.patch +$ git add gc.c gc.rb test/ruby/test_gc_compact.rb +$ git commit +``` + +Merge the squashed one commit on the +`wip/detect-compaction-runtime-tmp` branch +into the `wip/detect-compaction-runtime` branch. +``` +$ git cherry-pick +``` + +Fix conflicts seeing the difference by `git show ` +on another terminal. +``` +$ vi gc.c +$ git add gc.c +$ git commit +``` + +== Notes for the patch == + +``` ++# define GC_COMPACTION_SUPPORTED (GC_CAN_COMPILE_COMPACTION && USE_MMAP_ALIGNED_ALLOC) +``` + +We use the USE_MMAP_ALIGNED_ALLOC instead of HEAP_PAGE_ALLOC_USE_MMAP on +the line above. Because while the Ruby on the master branch replaced the +USE_MMAP_ALIGNED_ALLOC with HEAP_PAGE_ALLOC_USE_MMAP, Ruby 3.1.2 doesn't. +See . + +``` ++ rb_define_singleton_method(rb_mGC, "verify_compaction_references", gc_verify_compaction_references, -1); +``` + +We added the line in the case that GC_COMPACTION_SUPPORTED is true. +Because while the Ruby on the master branch defines the +GC.verify_compaction_references in the gc.rb in +the case that GC_COMPACTION_SUPPORTED is true, Ruby 3.1.2 +doesn't define it in the gc.rb. +See . + +``` ++ OPT(GC_COMPACTION_SUPPORTED); +``` + +We added the line to expose the C macro to Ruby level. +In Ruby the macro existance can then be checked like so: +```Ruby +GC::OPTS.include?("GC_COMPACTION_SUPPORTED") +``` +It will return `true` if the GC_COMPACTION_SUPPORTED evaluates to `true` on the +C level, `false` otherwise. +See + +== Original commit messages == + +This is a combination of 3 commits. +This is the 1st commit message: +~~~ +Rename GC_COMPACTION_SUPPORTED + +Naming this macro GC_COMPACTION_SUPPORTED is misleading because it +only checks whether compaction is supported at compile time. + +[Bug #18829] +~~~ + +This is the commit message #2: +~~~ +Include runtime checks for compaction support + +Commit 0c36ba53192c5a0d245c9b626e4346a32d7d144e changed GC compaction +methods to not be implemented when not supported. However, that commit +only does compile time checks (which currently only checks for WASM), +but there are additional compaction support checks during run time. + +This commit changes it so that GC compaction methods aren't defined +during run time if the platform does not support GC compaction. + +[Bug #18829] +~~~ + +This is the commit message #3: +~~~ +Suppress code unused unless GC_CAN_COMPILE_COMPACTION +~~~ +--- + gc.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 46 insertions(+), 12 deletions(-) + +diff --git a/gc.c b/gc.c +index 6a5c739cc4..a3e40d9aac 100644 +--- a/gc.c ++++ b/gc.c +@@ -4632,7 +4632,25 @@ gc_unprotect_pages(rb_objspace_t *objspace, rb_heap_t *heap) + static void gc_update_references(rb_objspace_t * objspace, rb_heap_t *heap); + static void invalidate_moved_page(rb_objspace_t *objspace, struct heap_page *page); + +-static void read_barrier_handler(intptr_t address) ++#ifndef GC_CAN_COMPILE_COMPACTION ++#if defined(__wasi__) /* WebAssembly doesn't support signals */ ++# define GC_CAN_COMPILE_COMPACTION 0 ++#else ++# define GC_CAN_COMPILE_COMPACTION 1 ++#endif ++#endif ++ ++#if defined(__MINGW32__) || defined(_WIN32) ++# define GC_COMPACTION_SUPPORTED 1 ++#else ++/* If not MinGW, Windows, or does not have mmap, we cannot use mprotect for ++ * the read barrier, so we must disable compaction. */ ++# define GC_COMPACTION_SUPPORTED (GC_CAN_COMPILE_COMPACTION && USE_MMAP_ALIGNED_ALLOC) ++#endif ++ ++#if GC_CAN_COMPILE_COMPACTION ++static void ++read_barrier_handler(uintptr_t address) + { + VALUE obj; + rb_objspace_t * objspace = &rb_objspace; +@@ -4651,6 +4669,7 @@ static void read_barrier_handler(intptr_t address) + } + RB_VM_LOCK_LEAVE(); + } ++#endif + + #if defined(_WIN32) + static LPTOP_LEVEL_EXCEPTION_FILTER old_handler; +@@ -8562,6 +8581,8 @@ gc_start_internal(rb_execution_context_t *ec, VALUE self, VALUE full_mark, VALUE + + /* For now, compact implies full mark / sweep, so ignore other flags */ + if (RTEST(compact)) { ++ GC_ASSERT(GC_COMPACTION_SUPPORTED); ++ + /* If not MinGW, Windows, or does not have mmap, we cannot use mprotect for + * the read barrier, so we must disable compaction. */ + #if !defined(__MINGW32__) && !defined(_WIN32) +@@ -8732,7 +8753,7 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free) + return (VALUE)src; + } + +-#if GC_COMPACTION_SUPPORTED ++#if GC_CAN_COMPILE_COMPACTION + static int + compare_free_slots(const void *left, const void *right, void *dummy) + { +@@ -9412,7 +9433,7 @@ gc_update_references(rb_objspace_t * objspace, rb_heap_t *heap) + + static VALUE type_sym(size_t type); + +-#if GC_COMPACTION_SUPPORTED ++#if GC_CAN_COMPILE_COMPACTION + /* + * call-seq: + * GC.latest_compact_info -> {:considered=>{:T_CLASS=>11}, :moved=>{:T_CLASS=>11}} +@@ -9453,7 +9474,7 @@ gc_compact_stats(VALUE self) + # define gc_compact_stats rb_f_notimplement + #endif + +-#if GC_COMPACTION_SUPPORTED ++#if GC_CAN_COMPILE_COMPACTION + static void + root_obj_check_moved_i(const char *category, VALUE obj, void *data) + { +@@ -9532,7 +9553,7 @@ gc_compact(VALUE self) + # define gc_compact rb_f_notimplement + #endif + +-#if GC_COMPACTION_SUPPORTED ++#if GC_CAN_COMPILE_COMPACTION + /* + * call-seq: + * GC.verify_compaction_references(toward: nil, double_heap: false) -> hash +@@ -10056,7 +10077,7 @@ gc_disable(rb_execution_context_t *ec, VALUE _) + return rb_gc_disable(); + } + +-#if GC_COMPACTION_SUPPORTED ++#if GC_CAN_COMPILE_COMPACTION + /* + * call-seq: + * GC.auto_compact = flag +@@ -10077,6 +10098,7 @@ gc_set_auto_compact(VALUE _, VALUE v) + rb_raise(rb_eNotImpError, "Automatic compaction isn't available on this platform"); + } + #endif ++ GC_ASSERT(GC_COMPACTION_SUPPORTED); + + ruby_enable_autocompact = RTEST(v); + return v; +@@ -10085,7 +10107,8 @@ gc_set_auto_compact(VALUE _, VALUE v) + # define gc_set_auto_compact rb_f_notimplement + #endif + +-#if GC_COMPACTION_SUPPORTED ++ ++#if GC_CAN_COMPILE_COMPACTION + /* + * call-seq: + * GC.auto_compact -> true or false +@@ -12926,11 +12949,21 @@ Init_GC(void) + rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0); + rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0); + #endif +- rb_define_singleton_method(rb_mGC, "compact", gc_compact, 0); +- rb_define_singleton_method(rb_mGC, "auto_compact", gc_get_auto_compact, 0); +- rb_define_singleton_method(rb_mGC, "auto_compact=", gc_set_auto_compact, 1); +- rb_define_singleton_method(rb_mGC, "latest_compact_info", gc_compact_stats, 0); +- rb_define_singleton_method(rb_mGC, "verify_compaction_references", gc_verify_compaction_references, -1); ++ if (GC_COMPACTION_SUPPORTED) { ++ rb_define_singleton_method(rb_mGC, "compact", gc_compact, 0); ++ rb_define_singleton_method(rb_mGC, "auto_compact", gc_get_auto_compact, 0); ++ rb_define_singleton_method(rb_mGC, "auto_compact=", gc_set_auto_compact, 1); ++ rb_define_singleton_method(rb_mGC, "latest_compact_info", gc_compact_stats, 0); ++ rb_define_singleton_method(rb_mGC, "verify_compaction_references", gc_verify_compaction_references, -1); ++ } ++ else { ++ rb_define_singleton_method(rb_mGC, "compact", rb_f_notimplement, 0); ++ rb_define_singleton_method(rb_mGC, "auto_compact", rb_f_notimplement, 0); ++ rb_define_singleton_method(rb_mGC, "auto_compact=", rb_f_notimplement, 1); ++ rb_define_singleton_method(rb_mGC, "latest_compact_info", rb_f_notimplement, 0); ++ /* When !GC_COMPACTION_SUPPORTED, this method is not defined in gc.rb */ ++ rb_define_singleton_method(rb_mGC, "verify_compaction_references", rb_f_notimplement, -1); ++ } + + #if GC_DEBUG_STRESS_TO_CLASS + rb_define_singleton_method(rb_mGC, "add_stress_to_class", rb_gcdebug_add_stress_to_class, -1); +@@ -12954,6 +12987,7 @@ Init_GC(void) + OPT(MALLOC_ALLOCATED_SIZE); + OPT(MALLOC_ALLOCATED_SIZE_CHECK); + OPT(GC_PROFILE_DETAIL_MEMORY); ++ OPT(GC_COMPACTION_SUPPORTED); + #undef OPT + OBJ_FREEZE(opts); + } diff --git a/SOURCES/ruby-3.2.0-define-unsupported-gc-compaction-methods-as-rb_f_notimplement.patch b/SOURCES/ruby-3.2.0-define-unsupported-gc-compaction-methods-as-rb_f_notimplement.patch new file mode 100644 index 0000000..47854a9 --- /dev/null +++ b/SOURCES/ruby-3.2.0-define-unsupported-gc-compaction-methods-as-rb_f_notimplement.patch @@ -0,0 +1,493 @@ +From 2b48aa5d9d7bfcf80ea23c352ceb10f0cf4e4ee0 Mon Sep 17 00:00:00 2001 +From: Mike Dalessio +Date: Mon, 23 May 2022 15:40:22 -0400 +Subject: [PATCH 1/2] Move compaction-related methods into gc.c + +These methods are removed from gc.rb and added to gc.c: + +- GC.compact +- GC.auto_compact +- GC.auto_compact= +- GC.latest_compact_info +- GC.verify_compaction_references + +This is a prefactor to allow setting these methods to +`rb_f_notimplement` in a followup commit. +--- + gc.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ + gc.rb | 68 --------------------------------------- + 2 files changed, 91 insertions(+), 78 deletions(-) + +diff --git a/gc.c b/gc.c +index b821310934..ef0e66ee46 100644 +--- a/gc.c ++++ b/gc.c +@@ -9410,8 +9410,20 @@ gc_update_references(rb_objspace_t * objspace, rb_heap_t *heap) + + static VALUE type_sym(size_t type); + ++/* ++ * call-seq: ++ * GC.latest_compact_info -> {:considered=>{:T_CLASS=>11}, :moved=>{:T_CLASS=>11}} ++ * ++ * Returns information about object moved in the most recent GC compaction. ++ * ++ * The returned hash has two keys :considered and :moved. The hash for ++ * :considered lists the number of objects that were considered for movement ++ * by the compactor, and the :moved hash lists the number of objects that ++ * were actually moved. Some objects can't be moved (maybe they were pinned) ++ * so these numbers can be used to calculate compaction efficiency. ++ */ + static VALUE +-gc_compact_stats(rb_execution_context_t *ec, VALUE self) ++gc_compact_stats(VALUE self) + { + size_t i; + rb_objspace_t *objspace = &rb_objspace; +@@ -9484,22 +9496,70 @@ heap_check_moved_i(void *vstart, void *vend, size_t stride, void *data) + return 0; + } + ++/* ++ * call-seq: ++ * GC.compact ++ * ++ * This function compacts objects together in Ruby's heap. It eliminates ++ * unused space (or fragmentation) in the heap by moving objects in to that ++ * unused space. This function returns a hash which contains statistics about ++ * which objects were moved. See `GC.latest_gc_info` for details about ++ * compaction statistics. ++ * ++ * This method is implementation specific and not expected to be implemented ++ * in any implementation besides MRI. ++ */ + static VALUE +-gc_compact(rb_execution_context_t *ec, VALUE self) ++gc_compact(VALUE self) + { + /* Run GC with compaction enabled */ +- gc_start_internal(ec, self, Qtrue, Qtrue, Qtrue, Qtrue); ++ gc_start_internal(NULL, self, Qtrue, Qtrue, Qtrue, Qtrue); + +- return gc_compact_stats(ec, self); ++ return gc_compact_stats(self); + } + ++/* ++ * call-seq: ++ * GC.verify_compaction_references(toward: nil, double_heap: false) -> hash ++ * ++ * Verify compaction reference consistency. ++ * ++ * This method is implementation specific. During compaction, objects that ++ * were moved are replaced with T_MOVED objects. No object should have a ++ * reference to a T_MOVED object after compaction. ++ * ++ * This function doubles the heap to ensure room to move all objects, ++ * compacts the heap to make sure everything moves, updates all references, ++ * then performs a full GC. If any object contains a reference to a T_MOVED ++ * object, that object should be pushed on the mark stack, and will ++ * make a SEGV. ++ */ + static VALUE +-gc_verify_compaction_references(rb_execution_context_t *ec, VALUE self, VALUE double_heap, VALUE toward_empty) ++gc_verify_compaction_references(int argc, VALUE *argv, VALUE self) + { + rb_objspace_t *objspace = &rb_objspace; ++ VALUE kwargs, double_heap = Qfalse, toward_empty = Qfalse; ++ static ID id_toward, id_double_heap, id_empty; ++ ++ if (!id_toward) { ++ id_toward = rb_intern("toward"); ++ id_double_heap = rb_intern("double_heap"); ++ id_empty = rb_intern("empty"); ++ } ++ ++ rb_scan_args(argc, argv, ":", &kwargs); ++ if (!NIL_P(kwargs)) { ++ if (rb_hash_has_key(kwargs, ID2SYM(id_toward))) { ++ VALUE toward = rb_hash_aref(kwargs, ID2SYM(id_toward)); ++ toward_empty = (toward == ID2SYM(id_empty)) ? Qtrue : Qfalse; ++ } ++ if (rb_hash_has_key(kwargs, ID2SYM(id_double_heap))) { ++ double_heap = rb_hash_aref(kwargs, ID2SYM(id_double_heap)); ++ } ++ } + + /* Clear the heap. */ +- gc_start_internal(ec, self, Qtrue, Qtrue, Qtrue, Qfalse); ++ gc_start_internal(NULL, self, Qtrue, Qtrue, Qtrue, Qfalse); + + RB_VM_LOCK_ENTER(); + { +@@ -9515,12 +9575,12 @@ gc_verify_compaction_references(rb_execution_context_t *ec, VALUE self, VALUE do + } + RB_VM_LOCK_LEAVE(); + +- gc_start_internal(ec, self, Qtrue, Qtrue, Qtrue, Qtrue); ++ gc_start_internal(NULL, self, Qtrue, Qtrue, Qtrue, Qtrue); + + objspace_reachable_objects_from_root(objspace, root_obj_check_moved_i, NULL); + objspace_each_objects(objspace, heap_check_moved_i, NULL); + +- return gc_compact_stats(ec, self); ++ return gc_compact_stats(self); + } + + VALUE +@@ -9978,8 +10038,18 @@ gc_disable(rb_execution_context_t *ec, VALUE _) + return rb_gc_disable(); + } + ++/* ++ * call-seq: ++ * GC.auto_compact = flag ++ * ++ * Updates automatic compaction mode. ++ * ++ * When enabled, the compactor will execute on every major collection. ++ * ++ * Enabling compaction will degrade performance on major collections. ++ */ + static VALUE +-gc_set_auto_compact(rb_execution_context_t *ec, VALUE _, VALUE v) ++gc_set_auto_compact(VALUE _, VALUE v) + { + /* If not MinGW, Windows, or does not have mmap, we cannot use mprotect for + * the read barrier, so we must disable automatic compaction. */ +@@ -9993,8 +10063,14 @@ gc_set_auto_compact(rb_execution_context_t *ec, VALUE _, VALUE v) + return v; + } + ++/* ++ * call-seq: ++ * GC.auto_compact -> true or false ++ * ++ * Returns whether or not automatic compaction has been enabled. ++ */ + static VALUE +-gc_get_auto_compact(rb_execution_context_t *ec, VALUE _) ++gc_get_auto_compact(VALUE _) + { + return ruby_enable_autocompact ? Qtrue : Qfalse; + } +@@ -12824,6 +12900,11 @@ Init_GC(void) + rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0); + rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0); + #endif ++ rb_define_singleton_method(rb_mGC, "compact", gc_compact, 0); ++ rb_define_singleton_method(rb_mGC, "auto_compact", gc_get_auto_compact, 0); ++ rb_define_singleton_method(rb_mGC, "auto_compact=", gc_set_auto_compact, 1); ++ rb_define_singleton_method(rb_mGC, "latest_compact_info", gc_compact_stats, 0); ++ rb_define_singleton_method(rb_mGC, "verify_compaction_references", gc_verify_compaction_references, -1); + + #if GC_DEBUG_STRESS_TO_CLASS + rb_define_singleton_method(rb_mGC, "add_stress_to_class", rb_gcdebug_add_stress_to_class, -1); +diff --git a/gc.rb b/gc.rb +index 8a00b406ce..4a80e09443 100644 +--- a/gc.rb ++++ b/gc.rb +@@ -38,27 +38,6 @@ def garbage_collect full_mark: true, immediate_mark: true, immediate_sweep: true + Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false + end + +- # call-seq: +- # GC.auto_compact -> true or false +- # +- # Returns whether or not automatic compaction has been enabled. +- # +- def self.auto_compact +- Primitive.gc_get_auto_compact +- end +- +- # call-seq: +- # GC.auto_compact = flag +- # +- # Updates automatic compaction mode. +- # +- # When enabled, the compactor will execute on every major collection. +- # +- # Enabling compaction will degrade performance on major collections. +- def self.auto_compact=(flag) +- Primitive.gc_set_auto_compact(flag) +- end +- + # call-seq: + # GC.enable -> true or false + # +@@ -183,53 +162,6 @@ def self.stat hash_or_key = nil + def self.latest_gc_info hash_or_key = nil + Primitive.gc_latest_gc_info hash_or_key + end +- +- # call-seq: +- # GC.latest_compact_info -> {:considered=>{:T_CLASS=>11}, :moved=>{:T_CLASS=>11}} +- # +- # Returns information about object moved in the most recent GC compaction. +- # +- # The returned hash has two keys :considered and :moved. The hash for +- # :considered lists the number of objects that were considered for movement +- # by the compactor, and the :moved hash lists the number of objects that +- # were actually moved. Some objects can't be moved (maybe they were pinned) +- # so these numbers can be used to calculate compaction efficiency. +- def self.latest_compact_info +- Primitive.gc_compact_stats +- end +- +- # call-seq: +- # GC.compact +- # +- # This function compacts objects together in Ruby's heap. It eliminates +- # unused space (or fragmentation) in the heap by moving objects in to that +- # unused space. This function returns a hash which contains statistics about +- # which objects were moved. See `GC.latest_gc_info` for details about +- # compaction statistics. +- # +- # This method is implementation specific and not expected to be implemented +- # in any implementation besides MRI. +- def self.compact +- Primitive.gc_compact +- end +- +- # call-seq: +- # GC.verify_compaction_references(toward: nil, double_heap: false) -> hash +- # +- # Verify compaction reference consistency. +- # +- # This method is implementation specific. During compaction, objects that +- # were moved are replaced with T_MOVED objects. No object should have a +- # reference to a T_MOVED object after compaction. +- # +- # This function doubles the heap to ensure room to move all objects, +- # compacts the heap to make sure everything moves, updates all references, +- # then performs a full GC. If any object contains a reference to a T_MOVED +- # object, that object should be pushed on the mark stack, and will +- # make a SEGV. +- def self.verify_compaction_references(toward: nil, double_heap: false) +- Primitive.gc_verify_compaction_references(double_heap, toward == :empty) +- end + end + + module ObjectSpace + +From 5c73c78da0ffca162b2dad6f217ae0ec1565ad52 Mon Sep 17 00:00:00 2001 +From: Mike Dalessio +Date: Mon, 23 May 2022 17:31:14 -0400 +Subject: [PATCH 2/2] Define unsupported GC compaction methods as + rb_f_notimplement + +Fixes [Bug #18779] + +Define the following methods as `rb_f_notimplement` on unsupported +platforms: + +- GC.compact +- GC.auto_compact +- GC.auto_compact= +- GC.latest_compact_info +- GC.verify_compaction_references + +This change allows users to call `GC.respond_to?(:compact)` to +properly test for compaction support. Previously, it was necessary to +invoke `GC.compact` or `GC.verify_compaction_references` and check if +those methods raised `NotImplementedError` to determine if compaction +was supported. + +This follows the precedent set for other platform-specific +methods. For example, in `process.c` for methods such as +`Process.fork`, `Process.setpgid`, and `Process.getpriority`. +--- + gc.c | 26 ++++++++++++++++ + test/ruby/test_gc_compact.rb | 58 ++++++++++++++++++++++++++---------- + 2 files changed, 69 insertions(+), 15 deletions(-) + +diff --git a/gc.c b/gc.c +index ef0e66ee46..6a5c739cc4 100644 +--- a/gc.c ++++ b/gc.c +@@ -8732,6 +8732,7 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free) + return (VALUE)src; + } + ++#if GC_COMPACTION_SUPPORTED + static int + compare_free_slots(const void *left, const void *right, void *dummy) + { +@@ -8775,6 +8776,7 @@ gc_sort_heap_by_empty_slots(rb_objspace_t *objspace) + + free(page_list); + } ++#endif + + static void + gc_ref_update_array(rb_objspace_t * objspace, VALUE v) +@@ -9410,6 +9412,7 @@ gc_update_references(rb_objspace_t * objspace, rb_heap_t *heap) + + static VALUE type_sym(size_t type); + ++#if GC_COMPACTION_SUPPORTED + /* + * call-seq: + * GC.latest_compact_info -> {:considered=>{:T_CLASS=>11}, :moved=>{:T_CLASS=>11}} +@@ -9446,7 +9449,11 @@ gc_compact_stats(VALUE self) + + return h; + } ++#else ++# define gc_compact_stats rb_f_notimplement ++#endif + ++#if GC_COMPACTION_SUPPORTED + static void + root_obj_check_moved_i(const char *category, VALUE obj, void *data) + { +@@ -9508,6 +9515,10 @@ heap_check_moved_i(void *vstart, void *vend, size_t stride, void *data) + * + * This method is implementation specific and not expected to be implemented + * in any implementation besides MRI. ++ * ++ * To test whether GC compaction is supported, use the idiom: ++ * ++ * GC.respond_to?(:compact) + */ + static VALUE + gc_compact(VALUE self) +@@ -9517,7 +9528,11 @@ gc_compact(VALUE self) + + return gc_compact_stats(self); + } ++#else ++# define gc_compact rb_f_notimplement ++#endif + ++#if GC_COMPACTION_SUPPORTED + /* + * call-seq: + * GC.verify_compaction_references(toward: nil, double_heap: false) -> hash +@@ -9582,6 +9597,9 @@ gc_verify_compaction_references(int argc, VALUE *argv, VALUE self) + + return gc_compact_stats(self); + } ++#else ++# define gc_verify_compaction_references rb_f_notimplement ++#endif + + VALUE + rb_gc_start(void) +@@ -10038,6 +10056,7 @@ gc_disable(rb_execution_context_t *ec, VALUE _) + return rb_gc_disable(); + } + ++#if GC_COMPACTION_SUPPORTED + /* + * call-seq: + * GC.auto_compact = flag +@@ -10062,7 +10081,11 @@ gc_set_auto_compact(VALUE _, VALUE v) + ruby_enable_autocompact = RTEST(v); + return v; + } ++#else ++# define gc_set_auto_compact rb_f_notimplement ++#endif + ++#if GC_COMPACTION_SUPPORTED + /* + * call-seq: + * GC.auto_compact -> true or false +@@ -10074,6 +10097,9 @@ gc_get_auto_compact(VALUE _) + { + return ruby_enable_autocompact ? Qtrue : Qfalse; + } ++#else ++# define gc_get_auto_compact rb_f_notimplement ++#endif + + static int + get_envparam_size(const char *name, size_t *default_value, size_t lower_bound) +diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb +index f5cab55ba7..c9c96a7c9c 100644 +--- a/test/ruby/test_gc_compact.rb ++++ b/test/ruby/test_gc_compact.rb +@@ -4,14 +4,7 @@ + require 'etc' + + class TestGCCompact < Test::Unit::TestCase +- module SupportsCompact +- def setup +- skip "autocompact not supported on this platform" unless supports_auto_compact? +- super +- end +- +- private +- ++ module CompactionSupportInspector + def supports_auto_compact? + return true unless defined?(Etc::SC_PAGE_SIZE) + +@@ -25,10 +18,19 @@ def supports_auto_compact? + end + end + +- include SupportsCompact ++ module OmitUnlessCompactSupported ++ include CompactionSupportInspector ++ ++ def setup ++ omit "autocompact not supported on this platform" unless supports_auto_compact? ++ super ++ end ++ end ++ ++ include OmitUnlessCompactSupported + + class AutoCompact < Test::Unit::TestCase +- include SupportsCompact ++ include OmitUnlessCompactSupported + + def test_enable_autocompact + before = GC.auto_compact +@@ -81,13 +83,39 @@ def test_implicit_compaction_does_something + end + end + +- def os_page_size +- return true unless defined?(Etc::SC_PAGE_SIZE) ++ class CompactMethodsNotImplemented < Test::Unit::TestCase ++ include CompactionSupportInspector ++ ++ def assert_not_implemented(method, *args) ++ omit "autocompact is supported on this platform" if supports_auto_compact? ++ ++ assert_raise(NotImplementedError) { GC.send(method, *args) } ++ refute(GC.respond_to?(method), "GC.#{method} should be defined as rb_f_notimplement") ++ end ++ ++ def test_gc_compact_not_implemented ++ assert_not_implemented(:compact) ++ end ++ ++ def test_gc_auto_compact_get_not_implemented ++ assert_not_implemented(:auto_compact) ++ end ++ ++ def test_gc_auto_compact_set_not_implemented ++ assert_not_implemented(:auto_compact=, true) ++ end ++ ++ def test_gc_latest_compact_info_not_implemented ++ assert_not_implemented(:latest_compact_info) ++ end ++ ++ def test_gc_verify_compaction_references_not_implemented ++ assert_not_implemented(:verify_compaction_references) ++ end + end + +- def setup +- skip "autocompact not supported on this platform" unless supports_auto_compact? +- super ++ def os_page_size ++ return true unless defined?(Etc::SC_PAGE_SIZE) + end + + def test_gc_compact_stats diff --git a/SOURCES/ruby-3.2.0-define-unsupported-gc-compaction-methods_generated-files.patch b/SOURCES/ruby-3.2.0-define-unsupported-gc-compaction-methods_generated-files.patch new file mode 100644 index 0000000..1bf7242 --- /dev/null +++ b/SOURCES/ruby-3.2.0-define-unsupported-gc-compaction-methods_generated-files.patch @@ -0,0 +1,336 @@ +--- ruby-3.0.7/gc.rbinc 2024-04-23 12:23:28.000000000 +0200 ++++ ruby/gc.rbinc 2025-02-27 13:20:54.803133890 +0100 +@@ -26,31 +26,6 @@ + } + + static void +-mjit_compile_invokebuiltin_for_gc_get_auto_compact(FILE *f, long index, unsigned stack_size, bool inlinable_p) +-{ +- fprintf(f, " VALUE self = GET_SELF();\n"); +- fprintf(f, " typedef VALUE (*func)(rb_execution_context_t *, VALUE);\n"); +- fprintf(f, " func f = (func)%"PRIdPTR"; /* == gc_get_auto_compact */\n", (intptr_t)gc_get_auto_compact); +- fprintf(f, " val = f(ec, self);\n"); +-} +- +-static void +-mjit_compile_invokebuiltin_for_gc_set_auto_compact(FILE *f, long index, unsigned stack_size, bool inlinable_p) +-{ +- fprintf(f, " VALUE self = GET_SELF();\n"); +- fprintf(f, " typedef VALUE (*func)(rb_execution_context_t *, VALUE, VALUE);\n"); +- if (index == -1) { +- fprintf(f, " const VALUE *argv = &stack[%d];\n", stack_size - 1); +- } +- else { +- fprintf(f, " const unsigned int lnum = GET_ISEQ()->body->local_table_size;\n"); +- fprintf(f, " const VALUE *argv = GET_EP() - lnum - VM_ENV_DATA_SIZE + 1 + %ld;\n", index); +- } +- fprintf(f, " func f = (func)%"PRIdPTR"; /* == gc_set_auto_compact */\n", (intptr_t)gc_set_auto_compact); +- fprintf(f, " val = f(ec, self, argv[0]);\n"); +-} +- +-static void + mjit_compile_invokebuiltin_for_gc_enable(FILE *f, long index, unsigned stack_size, bool inlinable_p) + { + fprintf(f, " VALUE self = GET_SELF();\n"); +@@ -134,57 +109,18 @@ + fprintf(f, " val = f(ec, self, argv[0]);\n"); + } + +-static void +-mjit_compile_invokebuiltin_for_gc_compact_stats(FILE *f, long index, unsigned stack_size, bool inlinable_p) +-{ +- fprintf(f, " VALUE self = GET_SELF();\n"); +- fprintf(f, " typedef VALUE (*func)(rb_execution_context_t *, VALUE);\n"); +- fprintf(f, " func f = (func)%"PRIdPTR"; /* == gc_compact_stats */\n", (intptr_t)gc_compact_stats); +- fprintf(f, " val = f(ec, self);\n"); +-} +- +-static void +-mjit_compile_invokebuiltin_for_gc_compact(FILE *f, long index, unsigned stack_size, bool inlinable_p) +-{ +- fprintf(f, " VALUE self = GET_SELF();\n"); +- fprintf(f, " typedef VALUE (*func)(rb_execution_context_t *, VALUE);\n"); +- fprintf(f, " func f = (func)%"PRIdPTR"; /* == gc_compact */\n", (intptr_t)gc_compact); +- fprintf(f, " val = f(ec, self);\n"); +-} +- +-static void +-mjit_compile_invokebuiltin_for_gc_verify_compaction_references(FILE *f, long index, unsigned stack_size, bool inlinable_p) +-{ +- fprintf(f, " VALUE self = GET_SELF();\n"); +- fprintf(f, " typedef VALUE (*func)(rb_execution_context_t *, VALUE, VALUE, VALUE);\n"); +- if (index == -1) { +- fprintf(f, " const VALUE *argv = &stack[%d];\n", stack_size - 2); +- } +- else { +- fprintf(f, " const unsigned int lnum = GET_ISEQ()->body->local_table_size;\n"); +- fprintf(f, " const VALUE *argv = GET_EP() - lnum - VM_ENV_DATA_SIZE + 1 + %ld;\n", index); +- } +- fprintf(f, " func f = (func)%"PRIdPTR"; /* == gc_verify_compaction_references */\n", (intptr_t)gc_verify_compaction_references); +- fprintf(f, " val = f(ec, self, argv[0], argv[1]);\n"); +-} +- + void Init_builtin_gc(void) + { + // table definition + static const struct rb_builtin_function gc_table[] = { + RB_BUILTIN_FUNCTION(0, gc_start_internal, gc_start_internal, 4, mjit_compile_invokebuiltin_for_gc_start_internal), +- RB_BUILTIN_FUNCTION(1, gc_get_auto_compact, gc_get_auto_compact, 0, mjit_compile_invokebuiltin_for_gc_get_auto_compact), +- RB_BUILTIN_FUNCTION(2, gc_set_auto_compact, gc_set_auto_compact, 1, mjit_compile_invokebuiltin_for_gc_set_auto_compact), +- RB_BUILTIN_FUNCTION(3, gc_enable, gc_enable, 0, mjit_compile_invokebuiltin_for_gc_enable), +- RB_BUILTIN_FUNCTION(4, gc_disable, gc_disable, 0, mjit_compile_invokebuiltin_for_gc_disable), +- RB_BUILTIN_FUNCTION(5, gc_stress_get, gc_stress_get, 0, mjit_compile_invokebuiltin_for_gc_stress_get), +- RB_BUILTIN_FUNCTION(6, gc_stress_set_m, gc_stress_set_m, 1, mjit_compile_invokebuiltin_for_gc_stress_set_m), +- RB_BUILTIN_FUNCTION(7, gc_count, gc_count, 0, mjit_compile_invokebuiltin_for_gc_count), +- RB_BUILTIN_FUNCTION(8, gc_stat, gc_stat, 1, mjit_compile_invokebuiltin_for_gc_stat), +- RB_BUILTIN_FUNCTION(9, gc_latest_gc_info, gc_latest_gc_info, 1, mjit_compile_invokebuiltin_for_gc_latest_gc_info), +- RB_BUILTIN_FUNCTION(10, gc_compact_stats, gc_compact_stats, 0, mjit_compile_invokebuiltin_for_gc_compact_stats), +- RB_BUILTIN_FUNCTION(11, gc_compact, gc_compact, 0, mjit_compile_invokebuiltin_for_gc_compact), +- RB_BUILTIN_FUNCTION(12, gc_verify_compaction_references, gc_verify_compaction_references, 2, mjit_compile_invokebuiltin_for_gc_verify_compaction_references), ++ RB_BUILTIN_FUNCTION(1, gc_enable, gc_enable, 0, mjit_compile_invokebuiltin_for_gc_enable), ++ RB_BUILTIN_FUNCTION(2, gc_disable, gc_disable, 0, mjit_compile_invokebuiltin_for_gc_disable), ++ RB_BUILTIN_FUNCTION(3, gc_stress_get, gc_stress_get, 0, mjit_compile_invokebuiltin_for_gc_stress_get), ++ RB_BUILTIN_FUNCTION(4, gc_stress_set_m, gc_stress_set_m, 1, mjit_compile_invokebuiltin_for_gc_stress_set_m), ++ RB_BUILTIN_FUNCTION(5, gc_count, gc_count, 0, mjit_compile_invokebuiltin_for_gc_count), ++ RB_BUILTIN_FUNCTION(6, gc_stat, gc_stat, 1, mjit_compile_invokebuiltin_for_gc_stat), ++ RB_BUILTIN_FUNCTION(7, gc_latest_gc_info, gc_latest_gc_info, 1, mjit_compile_invokebuiltin_for_gc_latest_gc_info), + RB_BUILTIN_FUNCTION(-1, NULL, NULL, 0, 0), + }; + +@@ -194,8 +130,6 @@ + COMPILER_WARNING_ERROR(-Wincompatible-pointer-types) + #endif + if (0) rb_builtin_function_check_arity4(gc_start_internal); +- if (0) rb_builtin_function_check_arity0(gc_get_auto_compact); +- if (0) rb_builtin_function_check_arity1(gc_set_auto_compact); + if (0) rb_builtin_function_check_arity0(gc_enable); + if (0) rb_builtin_function_check_arity0(gc_disable); + if (0) rb_builtin_function_check_arity0(gc_stress_get); +@@ -203,9 +137,6 @@ + if (0) rb_builtin_function_check_arity0(gc_count); + if (0) rb_builtin_function_check_arity1(gc_stat); + if (0) rb_builtin_function_check_arity1(gc_latest_gc_info); +- if (0) rb_builtin_function_check_arity0(gc_compact_stats); +- if (0) rb_builtin_function_check_arity0(gc_compact); +- if (0) rb_builtin_function_check_arity2(gc_verify_compaction_references); + COMPILER_WARNING_POP + + // load +--- ruby-3.0.7/miniprelude.c 2024-04-23 12:23:28.000000000 +0200 ++++ ruby/miniprelude.c 2025-02-27 13:20:54.853135007 +0100 +@@ -320,10 +320,9 @@ + + static const char prelude_name2[] = ""; + static const struct { +- char L0[489]; /* 1..58 */ +- char L58[486]; /* 59..182 */ +- char L182[430]; /* 183..235 */ +- char L235[211]; /* 236..242 */ ++ char L0[502]; /* 1..70 */ ++ char L70[435]; /* 71..167 */ ++ char L167[211]; /* 168..174 */ + } prelude_code2 = { + #line 1 "gc.rb" + "\n"/* for gc.c */ +@@ -367,29 +366,6 @@ + " end\n" + "\n" + "\n"/* call-seq: */ +-"\n"/* GC.auto_compact -> true or false */ +-"\n"/* */ +-"\n"/* Returns whether or not automatic compaction has been enabled. */ +-"\n"/* */ +-" def self.auto_compact\n" +-" Primitive.gc_get_auto_compact\n" +-" end\n" +-"\n" +-"\n"/* call-seq: */ +-"\n"/* GC.auto_compact = flag */ +-"\n"/* */ +-"\n"/* Updates automatic compaction mode. */ +-"\n"/* */ +-"\n"/* When enabled, the compactor will execute on every major collection. */ +-"\n"/* */ +-"\n"/* Enabling compaction will degrade performance on major collections. */ +-" def self.auto_compact=(flag)\n" +-, +-#line 59 "gc.rb" +-" Primitive.gc_set_auto_compact(flag)\n" +-" end\n" +-"\n" +-"\n"/* call-seq: */ + "\n"/* GC.enable -> true or false */ + "\n"/* */ + "\n"/* Enables garbage collection, returning +true+ if garbage */ +@@ -419,6 +395,8 @@ + "\n"/* GC.stress -> integer, true or false */ + "\n"/* */ + "\n"/* Returns current status of GC stress mode. */ ++, ++#line 71 "gc.rb" + " def self.stress\n" + " Primitive.gc_stress_get\n" + " end\n" +@@ -510,70 +488,21 @@ + "\n"/* If the optional argument, hash, is given, */ + "\n"/* it is overwritten and returned. */ + "\n"/* This is intended to avoid probe effect. */ +-, +-#line 183 "gc.rb" + " def self.latest_gc_info hash_or_key = nil\n" + " Primitive.gc_latest_gc_info hash_or_key\n" + " end\n" +-"\n" +-"\n"/* call-seq: */ +-"\n"/* GC.latest_compact_info -> {:considered=>{:T_CLASS=>11}, :moved=>{:T_CLASS=>11}} */ +-"\n"/* */ +-"\n"/* Returns information about object moved in the most recent GC compaction. */ +-"\n"/* */ +-"\n"/* The returned hash has two keys :considered and :moved. The hash for */ +-"\n"/* :considered lists the number of objects that were considered for movement */ +-"\n"/* by the compactor, and the :moved hash lists the number of objects that */ +-"\n"/* were actually moved. Some objects can't be moved (maybe they were pinned) */ +-"\n"/* so these numbers can be used to calculate compaction efficiency. */ +-" def self.latest_compact_info\n" +-" Primitive.gc_compact_stats\n" +-" end\n" +-"\n" +-"\n"/* call-seq: */ +-"\n"/* GC.compact */ +-"\n"/* */ +-"\n"/* This function compacts objects together in Ruby's heap. It eliminates */ +-"\n"/* unused space (or fragmentation) in the heap by moving objects in to that */ +-"\n"/* unused space. This function returns a hash which contains statistics about */ +-"\n"/* which objects were moved. See `GC.latest_gc_info` for details about */ +-"\n"/* compaction statistics. */ +-"\n"/* */ +-"\n"/* This method is implementation specific and not expected to be implemented */ +-"\n"/* in any implementation besides MRI. */ +-" def self.compact\n" +-" Primitive.gc_compact\n" +-" end\n" +-"\n" +-"\n"/* call-seq: */ +-"\n"/* GC.verify_compaction_references(toward: nil, double_heap: false) -> hash */ +-"\n"/* */ +-"\n"/* Verify compaction reference consistency. */ +-"\n"/* */ +-"\n"/* This method is implementation specific. During compaction, objects that */ +-"\n"/* were moved are replaced with T_MOVED objects. No object should have a */ +-"\n"/* reference to a T_MOVED object after compaction. */ +-"\n"/* */ +-"\n"/* This function doubles the heap to ensure room to move all objects, */ +-"\n"/* compacts the heap to make sure everything moves, updates all references, */ +-"\n"/* then performs a full GC. If any object contains a reference to a T_MOVED */ +-"\n"/* object, that object should be pushed on the mark stack, and will */ +-"\n"/* make a SEGV. */ +-" def self.verify_compaction_references(toward: nil, double_heap: false)\n" +-" Primitive.gc_verify_compaction_references(double_heap, toward == :empty)\n" +-" end\n" + "end\n" + "\n" + "module ObjectSpace\n" + , +-#line 236 "gc.rb" ++#line 168 "gc.rb" + " def garbage_collect full_mark: true, immediate_mark: true, immediate_sweep: true\n" + " Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false\n" + " end\n" + "\n" + " module_function :garbage_collect\n" + "end\n" +-#line 577 "miniprelude.c" ++#line 506 "miniprelude.c" + }; + + static const char prelude_name3[] = ""; +@@ -734,7 +663,7 @@ + " Primitive.cexpr! 'rb_int_zero_p(self)'\n" + " end\n" + "end\n" +-#line 738 "miniprelude.c" ++#line 667 "miniprelude.c" + }; + + static const char prelude_name4[] = ""; +@@ -865,7 +794,7 @@ + " Primitive.io_write_nonblock(buf, exception)\n" + " end\n" + "end\n" +-#line 869 "miniprelude.c" ++#line 798 "miniprelude.c" + }; + + static const char prelude_name5[] = ""; +@@ -1156,7 +1085,7 @@ + " Primitive.pack_unpack1(fmt)\n" + " end\n" + "end\n" +-#line 1160 "miniprelude.c" ++#line 1089 "miniprelude.c" + }; + + static const char prelude_name6[] = ""; +@@ -1521,7 +1450,7 @@ + " Primitive.tracepoint_attr_instruction_sequence\n" + " end\n" + "end\n" +-#line 1525 "miniprelude.c" ++#line 1454 "miniprelude.c" + }; + + static const char prelude_name7[] = ""; +@@ -1582,7 +1511,7 @@ + " Primitive.rb_warn_m(msgs, uplevel, category)\n" + " end\n" + "end\n" +-#line 1586 "miniprelude.c" ++#line 1515 "miniprelude.c" + }; + + static const char prelude_name8[] = ""; +@@ -1651,7 +1580,7 @@ + " Primitive.rb_ary_sample(random, n, ary)\n" + " end\n" + "end\n" +-#line 1655 "miniprelude.c" ++#line 1584 "miniprelude.c" + }; + + static const char prelude_name9[] = ""; +@@ -1836,7 +1765,7 @@ + " Primitive.rb_f_float(arg, exception)\n" + " end\n" + "end\n" +-#line 1840 "miniprelude.c" ++#line 1769 "miniprelude.c" + }; + + static const char prelude_name10[] = ""; +@@ -2703,7 +2632,7 @@ + " }\n" + " end\n" + "end\n" +-#line 2707 "miniprelude.c" ++#line 2636 "miniprelude.c" + }; + + static const char prelude_name11[] = ""; +@@ -2733,7 +2662,7 @@ + "\n" + " private :pp\n" + "end\n" +-#line 2737 "miniprelude.c" ++#line 2666 "miniprelude.c" + }; + + static const char prelude_name12[] = ""; +@@ -2752,7 +2681,7 @@ + "rescue LoadError\n" + " warn \"`did_you_mean' was not loaded.\"\n" + "end if defined?(DidYouMean)\n" +-#line 2756 "miniprelude.c" ++#line 2685 "miniprelude.c" + }; + + COMPILER_WARNING_POP diff --git a/SOURCES/rubygem-cgi-0.3.5.1-Fix-DoS-in-CGI-Cookie-parse-CVE-2025-27219.patch b/SOURCES/rubygem-cgi-0.3.5.1-Fix-DoS-in-CGI-Cookie-parse-CVE-2025-27219.patch new file mode 100644 index 0000000..733bc24 --- /dev/null +++ b/SOURCES/rubygem-cgi-0.3.5.1-Fix-DoS-in-CGI-Cookie-parse-CVE-2025-27219.patch @@ -0,0 +1,27 @@ +From fd8162a42ff3e4004b940030cfe34ce7a44a7e23 Mon Sep 17 00:00:00 2001 +From: Hiroshi SHIBATA +Date: Fri, 21 Feb 2025 16:01:17 +0900 +Subject: [PATCH] Use String#concat instead of String#+ for reducing cpu usage + +Co-authored-by: "Yusuke Endoh" +--- + lib/cgi/cookie.rb | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb +index 1a9c1a82c1..7b8e761a94 100644 +--- a/lib/cgi/cookie.rb ++++ b/lib/cgi/cookie.rb +@@ -190,9 +190,10 @@ def self.parse(raw_cookie) + values ||= "" + values = values.split('&').collect{|v| CGI.unescape(v,@@accept_charset) } + if cookies.has_key?(name) +- values = cookies[name].value + values ++ cookies[name].concat(values) ++ else ++ cookies[name] = Cookie.new(name, *values) + end +- cookies[name] = Cookie.new(name, *values) + end + + cookies diff --git a/SOURCES/rubygem-cgi-0.3.5.1-Fix-ReDoS-in-CGI-CVE-2025-27220.patch b/SOURCES/rubygem-cgi-0.3.5.1-Fix-ReDoS-in-CGI-CVE-2025-27220.patch new file mode 100644 index 0000000..a7fdb00 --- /dev/null +++ b/SOURCES/rubygem-cgi-0.3.5.1-Fix-ReDoS-in-CGI-CVE-2025-27220.patch @@ -0,0 +1,68 @@ +From 725d6f699f589d9b5454e189d33292027532984d Mon Sep 17 00:00:00 2001 +From: Hiroshi SHIBATA +Date: Fri, 21 Feb 2025 15:53:31 +0900 +Subject: [PATCH] Escape/unescape unclosed tags as well + +Co-authored-by: Nobuyoshi Nakada +--- + lib/cgi/util.rb | 4 ++-- + test/cgi/test_cgi_util.rb | 18 ++++++++++++++++++ + 2 files changed, 20 insertions(+), 2 deletions(-) + +diff --git a/lib/cgi/util.rb b/lib/cgi/util.rb +index aab8b000cb..5ff8ba58eb 100644 +--- a/lib/cgi/util.rb ++++ b/lib/cgi/util.rb +@@ -140,7 +140,7 @@ def unescapeHTML(string) + def escapeElement(string, *elements) + elements = elements[0] if elements[0].kind_of?(Array) + unless elements.empty? +- string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/i) do ++ string.gsub(/<\/?(?:#{elements.join("|")})\b[^<>]*+>?/im) do + CGI.escapeHTML($&) + end + else +@@ -160,7 +160,7 @@ def escapeElement(string, *elements) + def unescapeElement(string, *elements) + elements = elements[0] if elements[0].kind_of?(Array) + unless elements.empty? +- string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/i) do ++ string.gsub(/<\/?(?:#{elements.join("|")})\b(?>[^&]+|&(?![gl]t;)\w+;)*(?:>)?/im) do + unescapeHTML($&) + end + else +diff --git a/test/cgi/test_cgi_util.rb b/test/cgi/test_cgi_util.rb +index b7bb7b8eae..e93be474b3 100644 +--- a/test/cgi/test_cgi_util.rb ++++ b/test/cgi/test_cgi_util.rb +@@ -181,6 +181,14 @@ def test_cgi_escapeElement + assert_equal("
<A HREF="url"></A>", escapeElement('
', ["A", "IMG"])) + assert_equal("
<A HREF="url"></A>", escape_element('
', "A", "IMG")) + assert_equal("
<A HREF="url"></A>", escape_element('
', ["A", "IMG"])) ++ ++ assert_equal("<A <A HREF="url"></A>", escapeElement('', "A", "IMG")) ++ assert_equal("<A <A HREF="url"></A>", escapeElement('', ["A", "IMG"])) ++ assert_equal("<A <A HREF="url"></A>", escape_element('', "A", "IMG")) ++ assert_equal("<A <A HREF="url"></A>", escape_element('', ["A", "IMG"])) ++ ++ assert_equal("<A <A ", escapeElement('', unescapeElement(escapeHTML('
'), ["A", "IMG"])) + assert_equal('<BR>', unescape_element(escapeHTML('
'), "A", "IMG")) + assert_equal('<BR>', unescape_element(escapeHTML('
'), ["A", "IMG"])) ++ ++ assert_equal('', unescapeElement(escapeHTML(''), "A", "IMG")) ++ assert_equal('', unescapeElement(escapeHTML(''), ["A", "IMG"])) ++ assert_equal('', unescape_element(escapeHTML(''), "A", "IMG")) ++ assert_equal('', unescape_element(escapeHTML(''), ["A", "IMG"])) ++ ++ assert_equal(' +Date: Sun, 16 Jan 2022 22:20:05 +0000 +Subject: [PATCH 1/3] Use require_relative to require lib files + +1. `require` can mislead Ruby to load system irb's files and cause + constant redefined warnings as other code loads the same module/class + from lib folder. +2. Most files already use `require_relative`. +--- + lib/irb/color.rb | 2 +- + lib/irb/color_printer.rb | 2 +- + lib/irb/inspector.rb | 2 +- + libexec/irb | 2 +- + 4 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/lib/irb/color.rb b/lib/irb/color.rb +index cfbb3cc668..4f6258adf8 100644 +--- a/lib/irb/color.rb ++++ b/lib/irb/color.rb +@@ -1,7 +1,7 @@ + # frozen_string_literal: true + require 'reline' + require 'ripper' +-require 'irb/ruby-lex' ++require_relative 'ruby-lex' + + module IRB # :nodoc: + module Color +diff --git a/lib/irb/color_printer.rb b/lib/irb/color_printer.rb +index 30c6825750..78f0b51520 100644 +--- a/lib/irb/color_printer.rb ++++ b/lib/irb/color_printer.rb +@@ -1,6 +1,6 @@ + # frozen_string_literal: true + require 'pp' +-require 'irb/color' ++require_relative 'color' + + module IRB + class ColorPrinter < ::PP +diff --git a/lib/irb/inspector.rb b/lib/irb/inspector.rb +index c2f3b605db..8c37c0f174 100644 +--- a/lib/irb/inspector.rb ++++ b/lib/irb/inspector.rb +@@ -114,7 +114,7 @@ def inspect_value(v) + end + result + } +- Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require "irb/color_printer"}){|v| ++ Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v| + if IRB.conf[:MAIN_CONTEXT]&.use_colorize? + IRB::ColorPrinter.pp(v, '').chomp + else +diff --git a/libexec/irb b/libexec/irb +index c64ee85fbd..ffc97867d0 100755 +--- a/libexec/irb ++++ b/libexec/irb +@@ -6,6 +6,6 @@ + # by Keiju ISHITSUKA(keiju@ruby-lang.org) + # + +-require "irb" ++require_relative '../lib/irb' + + IRB.start(__FILE__) + +From e885a5bf9d20b45737746c5f2ae91059c09830cc Mon Sep 17 00:00:00 2001 +From: st0012 +Date: Mon, 17 Jan 2022 11:45:16 +0000 +Subject: [PATCH 2/3] Use require_relative to load extensions/commands + +--- + lib/irb/extend-command.rb | 48 +++++++++++++++++++-------------------- + 1 file changed, 24 insertions(+), 24 deletions(-) + +diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb +index 339e9e6084..d6fb19038d 100644 +--- a/lib/irb/extend-command.rb ++++ b/lib/irb/extend-command.rb +@@ -47,7 +47,7 @@ def irb_context + + @EXTEND_COMMANDS = [ + [ +- :irb_current_working_workspace, :CurrentWorkingWorkspace, "irb/cmd/chws", ++ :irb_current_working_workspace, :CurrentWorkingWorkspace, "cmd/chws", + [:irb_print_working_workspace, OVERRIDE_ALL], + [:irb_cwws, OVERRIDE_ALL], + [:irb_pwws, OVERRIDE_ALL], +@@ -59,7 +59,7 @@ def irb_context + [:irb_pwb, OVERRIDE_ALL], + ], + [ +- :irb_change_workspace, :ChangeWorkspace, "irb/cmd/chws", ++ :irb_change_workspace, :ChangeWorkspace, "cmd/chws", + [:irb_chws, OVERRIDE_ALL], + [:irb_cws, OVERRIDE_ALL], + [:chws, NO_OVERRIDE], +@@ -70,13 +70,13 @@ def irb_context + ], + + [ +- :irb_workspaces, :Workspaces, "irb/cmd/pushws", ++ :irb_workspaces, :Workspaces, "cmd/pushws", + [:workspaces, NO_OVERRIDE], + [:irb_bindings, OVERRIDE_ALL], + [:bindings, NO_OVERRIDE], + ], + [ +- :irb_push_workspace, :PushWorkspace, "irb/cmd/pushws", ++ :irb_push_workspace, :PushWorkspace, "cmd/pushws", + [:irb_pushws, OVERRIDE_ALL], + [:pushws, NO_OVERRIDE], + [:irb_push_binding, OVERRIDE_ALL], +@@ -84,7 +84,7 @@ def irb_context + [:pushb, NO_OVERRIDE], + ], + [ +- :irb_pop_workspace, :PopWorkspace, "irb/cmd/pushws", ++ :irb_pop_workspace, :PopWorkspace, "cmd/pushws", + [:irb_popws, OVERRIDE_ALL], + [:popws, NO_OVERRIDE], + [:irb_pop_binding, OVERRIDE_ALL], +@@ -93,55 +93,55 @@ def irb_context + ], + + [ +- :irb_load, :Load, "irb/cmd/load"], ++ :irb_load, :Load, "cmd/load"], + [ +- :irb_require, :Require, "irb/cmd/load"], ++ :irb_require, :Require, "cmd/load"], + [ +- :irb_source, :Source, "irb/cmd/load", ++ :irb_source, :Source, "cmd/load", + [:source, NO_OVERRIDE], + ], + + [ +- :irb, :IrbCommand, "irb/cmd/subirb"], ++ :irb, :IrbCommand, "cmd/subirb"], + [ +- :irb_jobs, :Jobs, "irb/cmd/subirb", ++ :irb_jobs, :Jobs, "cmd/subirb", + [:jobs, NO_OVERRIDE], + ], + [ +- :irb_fg, :Foreground, "irb/cmd/subirb", ++ :irb_fg, :Foreground, "cmd/subirb", + [:fg, NO_OVERRIDE], + ], + [ +- :irb_kill, :Kill, "irb/cmd/subirb", ++ :irb_kill, :Kill, "cmd/subirb", + [:kill, OVERRIDE_PRIVATE_ONLY], + ], + + [ +- :irb_help, :Help, "irb/cmd/help", ++ :irb_help, :Help, "cmd/help", + [:help, NO_OVERRIDE], + ], + + [ +- :irb_info, :Info, "irb/cmd/info" ++ :irb_info, :Info, "cmd/info" + ], + + [ +- :irb_ls, :Ls, "irb/cmd/ls", ++ :irb_ls, :Ls, "cmd/ls", + [:ls, NO_OVERRIDE], + ], + + [ +- :irb_measure, :Measure, "irb/cmd/measure", ++ :irb_measure, :Measure, "cmd/measure", + [:measure, NO_OVERRIDE], + ], + + [ +- :irb_show_source, :ShowSource, "irb/cmd/show_source", ++ :irb_show_source, :ShowSource, "cmd/show_source", + [:show_source, NO_OVERRIDE], + ], + + [ +- :irb_whereami, :Whereami, "irb/cmd/whereami", ++ :irb_whereami, :Whereami, "cmd/whereami", + [:whereami, NO_OVERRIDE], + ], + +@@ -187,7 +187,7 @@ def self.def_extend_command(cmd_name, cmd_class, load_file = nil, *aliases) + kwargs = ", **kwargs" if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.7.0" + line = __LINE__; eval %[ + def #{cmd_name}(*opts#{kwargs}, &b) +- require "#{load_file}" ++ require_relative "#{load_file}" + arity = ExtendCommand::#{cmd_class}.instance_method(:execute).arity + args = (1..(arity < 0 ? ~arity : arity)).map {|i| "arg" + i.to_s } + args << "*opts#{kwargs}" if arity < 0 +@@ -262,10 +262,10 @@ module ContextExtender + CE = ContextExtender # :nodoc: + + @EXTEND_COMMANDS = [ +- [:eval_history=, "irb/ext/history.rb"], +- [:use_tracer=, "irb/ext/tracer.rb"], +- [:use_loader=, "irb/ext/use-loader.rb"], +- [:save_history=, "irb/ext/save-history.rb"], ++ [:eval_history=, "ext/history.rb"], ++ [:use_tracer=, "ext/tracer.rb"], ++ [:use_loader=, "ext/use-loader.rb"], ++ [:save_history=, "ext/save-history.rb"], + ] + + # Installs the default context extensions as irb commands: +@@ -288,7 +288,7 @@ def self.def_extend_command(cmd_name, load_file, *aliases) + line = __LINE__; Context.module_eval %[ + def #{cmd_name}(*opts, &b) + Context.module_eval {remove_method(:#{cmd_name})} +- require "#{load_file}" ++ require_relative "#{load_file}" + __send__ :#{cmd_name}, *opts, &b + end + for ali in aliases + +From 19431c7ccc23545b9e973d5e0993c20422f42796 Mon Sep 17 00:00:00 2001 +From: st0012 +Date: Mon, 17 Jan 2022 15:17:18 +0000 +Subject: [PATCH 3/3] require_relative can't be used for default gems' exe + files + +The `exe` folder and `lib` folder of default gems don't locate under the +same place. While `exe/irb` will be under the gem folder, `irb.rb` will be +under `lib/ruby/VERSION/`. + +So `require_relative` will make `irb` unuseable when shipped with Ruby. + +Related discussion in the comments: https://github.com/ruby/irb/pull/335 +--- + libexec/irb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libexec/irb b/libexec/irb +index ffc97867d0..c64ee85fbd 100755 +--- a/libexec/irb ++++ b/libexec/irb +@@ -6,6 +6,6 @@ + # by Keiju ISHITSUKA(keiju@ruby-lang.org) + # + +-require_relative '../lib/irb' ++require "irb" + + IRB.start(__FILE__) diff --git a/SPECS/ruby.spec b/SPECS/ruby.spec index d67859e..a3462b7 100644 --- a/SPECS/ruby.spec +++ b/SPECS/ruby.spec @@ -22,7 +22,7 @@ %endif -%global release 163 +%global release 165 %{!?release_string:%define release_string %{?development_release:0.}%{release}%{?development_release:.%{development_release}}%{?dist}} # The RubyGems library has to stay out of Ruby directory tree, since the @@ -300,6 +300,45 @@ Patch70: ruby-3.3.1-Fix-test-session-reuse-but-expire.patch # Tests not included, this Ruby release does not include REXML tests. # https://github.com/ruby/rexml/commit/ce59f2eb1aeb371fe1643414f06618dbe031979f Patch71: rubygem-rexml-3.3.9-Fix-ReDoS-CVE-2024-49761.patch +# If GC compaction is not supported on platform, define the +# corresponding GC methods as not implemented. +# https://bugs.ruby-lang.org/issues/18779 +# https://github.com/ruby/ruby/pull/5934 +Patch72: ruby-3.2.0-define-unsupported-gc-compaction-methods-as-rb_f_notimplement.patch +# tar -Jxvf ./ruby-3.0.7.tar.xz +# git clone https://github.com/ruby/ruby.git +# cd ruby && git checkout v3_0_7 +# patch -p1 < ../ruby-3.1.0-Use-mmap-for-allocating-heap-pages-in-the-GC.patch +# patch -p1 < ../ruby-3.2.0-define-unsupported-gc-compaction-methods-as-rb_f_notimplement.patch +# ./autogen.sh && ./configure +# make gc.rbinc miniprelude.c +# cd .. +# diff -u {ruby-3.0.7,ruby}/gc.rbinc > ruby-3.2.0-define-unsupported-gc-compaction-methods_generated-files.patch +# diff -u {ruby-3.0.7,ruby}/miniprelude.c >> ruby-3.2.0-define-unsupported-gc-compaction-methods_generated-files.patch +Patch73: ruby-3.2.0-define-unsupported-gc-compaction-methods_generated-files.patch +# Define the GC compaction support macro at run time. +# https://bugs.ruby-lang.org/issues/18829 +# https://github.com/ruby/ruby/pull/6019 +# https://github.com/ruby/ruby/commit/2c190863239bee3f54cfb74b16bb6ea4cae6ed20 +Patch74: ruby-3.2.0-Detect-compaction-support-during-runtime.patch +# IRB, when used from within ruby code using simple `require 'irb'; binding.irb`, +# a lot of warning messages about already initialized constants, similar to: +# "/usr/share/ruby/irb/ruby-lex.rb:123: warning: already initialized constant RubyLex::ERROR_TOKENS" +# "/usr/share/gems/gems/irb-1.3.5/lib/irb/ruby-lex.rb:123: warning: previous definition of ERROR_TOKENS was here" +# are printed. +# https://github.com/ruby/irb/pull/335 +# https://github.com/ruby/irb/commit/848d339f2e255f4b0dfc68568d818d4c3587b266 +# https://github.com/ruby/irb/pull/336 +# https://github.com/ruby/irb/commit/d5060f76687e3ff355583bfa5f073c67d558e2bd +# https://github.com/ruby/irb/pull/338 +# https://github.com/ruby/irb/commit/99d3aa979dffece1fab06a7d5ebff4ae5da50aae +Patch75: rubygem-irb-1.4.2-Fix-already-initialized-constant-messages-from-require-in-scripts.patch +# Fix Denial of Service in CGI::Cookie.parse. (CVE-2025-27219) +# https://github.com/ruby/cgi/commit/2f8ec73bb3eb71c4cf13e735f2d696603de2f34b +Patch76: rubygem-cgi-0.3.5.1-Fix-DoS-in-CGI-Cookie-parse-CVE-2025-27219.patch +# Fix ReDoS in CGI::Util#escapeElement. (CVE-2025-27220) +# https://github.com/ruby/cgi/commit/bfa69e120df4e0131bb05df6c5e05c1dc982cd37 +Patch77: rubygem-cgi-0.3.5.1-Fix-ReDoS-in-CGI-CVE-2025-27220.patch Requires: %{name}-libs%{?_isa} = %{version}-%{release} Suggests: rubypick @@ -773,6 +812,12 @@ rm -rf ext/fiddle/libffi* %patch68 -p1 %patch69 -p1 %patch70 -p1 +%patch72 -p1 +%patch73 -p1 +%patch74 -p1 +%patch75 -p1 +%patch76 -p1 +%patch77 -p1 # Instead of adjusting patch's directory, use the following form where # we first enter the correct directory, this allows more general application @@ -1555,6 +1600,18 @@ make runruby TESTRUN_SCRIPT=" \ %changelog +* Fri Apr 11 2025 Jarek Prokop - 3.0.7-165 +- Fix Denial of Service in CGI::Cookie.parse. (CVE-2025-27219) + Resolves: RHEL-87183 +- Fix ReDoS in CGI::Util#escapeElement. (CVE-2025-27220) + Resolves: RHEL-87184 + +* Thu Mar 06 2025 Jarek Prokop - 3.0.7-164 +- Undefine GC compaction methods on ppc64le. + Resolves: RHEL-83135 +- Fix printing warnings when using IRB from a script. + Resolves: RHEL-83137 + * Tue Nov 26 2024 Jarek Prokop - 3.0.7-163 - Fix REXML ReDoS vulnerability. (CVE-2024-49761) Resolves: RHEL-68521