* Upgrade to Ruby 3.1.5. Resolves: RHEL-35748 * Fix buffer overread vulnerability in StringIO. Resolves: RHEL-35749 * Fix RCE vulnerability with .rdoc_options in RDoc. Resolves: RHEL-35750 * Fix arbitrary memory address read vulnerability with Regex search. Resolves: RHEL-35751 Fix fiddle build with libffi versions 3.1 or older.
		
			
				
	
	
		
			503 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			503 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 1b3502156a665e2782f366aa5ac8c3bfd7637ab8 Mon Sep 17 00:00:00 2001
 | |
| From: Mike Dalessio <mike.dalessio@gmail.com>
 | |
| 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 ef9327df1f..1c35856c44 100644
 | |
| --- a/gc.c
 | |
| +++ b/gc.c
 | |
| @@ -10195,8 +10195,20 @@ gc_update_references(rb_objspace_t *objspace)
 | |
|      gc_update_table_refs(objspace, finalizer_table);
 | |
|  }
 | |
|  
 | |
| +/*
 | |
| + *  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;
 | |
| @@ -10269,22 +10281,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();
 | |
|      {
 | |
| @@ -10304,12 +10364,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, TRUE);
 | |
|  
 | |
| -    return gc_compact_stats(ec, self);
 | |
| +    return gc_compact_stats(self);
 | |
|  }
 | |
|  
 | |
|  VALUE
 | |
| @@ -10770,8 +10830,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. */
 | |
| @@ -10785,8 +10855,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 RBOOL(ruby_enable_autocompact);
 | |
|  }
 | |
| @@ -13694,6 +13770,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 72637f3796..9265dd7b57 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
 | |
|    #
 | |
| @@ -210,53 +189,6 @@ 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
 | |
| -
 | |
|    # call-seq:
 | |
|    #     GC.using_rvargc? -> true or false
 | |
|    #
 | |
| 
 | |
| From d3273559356db6852d1fd794f0f076fba100e09e Mon Sep 17 00:00:00 2001
 | |
| From: Mike Dalessio <mike.dalessio@gmail.com>
 | |
| 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                         | 31 +++++++++++++++----
 | |
|  test/ruby/test_gc_compact.rb | 58 ++++++++++++++++++++++++++----------
 | |
|  2 files changed, 69 insertions(+), 20 deletions(-)
 | |
| 
 | |
| diff --git a/gc.c b/gc.c
 | |
| index 92ed76cf96..d71924846a 100644
 | |
| --- a/gc.c
 | |
| +++ b/gc.c
 | |
| @@ -9442,6 +9442,7 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t slot_size)
 | |
|      return (VALUE)src;
 | |
|  }
 | |
|  
 | |
| +#if GC_COMPACTION_SUPPORTED
 | |
|  static int
 | |
|  compare_free_slots(const void *left, const void *right, void *dummy)
 | |
|  {
 | |
| @@ -9489,6 +9490,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)
 | |
| @@ -10195,6 +10197,7 @@ gc_update_references(rb_objspace_t *objspace)
 | |
|      gc_update_table_refs(objspace, finalizer_table);
 | |
|  }
 | |
|  
 | |
| +#if GC_COMPACTION_SUPPORTED
 | |
|  /*
 | |
|   *  call-seq:
 | |
|   *     GC.latest_compact_info -> {:considered=>{:T_CLASS=>11}, :moved=>{:T_CLASS=>11}}
 | |
| @@ -10231,7 +10234,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)
 | |
|  {
 | |
| @@ -10293,6 +10300,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)
 | |
| @@ -10302,7 +10313,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
 | |
| @@ -10371,6 +10386,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)
 | |
| @@ -10830,6 +10848,7 @@ gc_disable(rb_execution_context_t *ec, VALUE _)
 | |
|      return rb_gc_disable();
 | |
|  }
 | |
|  
 | |
| +#if GC_COMPACTION_SUPPORTED
 | |
|  /*
 | |
|   *  call-seq:
 | |
|   *     GC.auto_compact = flag
 | |
| @@ -10845,16 +10864,15 @@ 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. */
 | |
| -#if !defined(__MINGW32__) && !defined(_WIN32)
 | |
| -    if (!USE_MMAP_ALIGNED_ALLOC) {
 | |
| -        rb_raise(rb_eNotImpError, "Automatic compaction isn't available on this platform");
 | |
| -    }
 | |
| -#endif
 | |
|  
 | |
|      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
 | |
| @@ -10866,6 +10884,9 @@ gc_get_auto_compact(VALUE _)
 | |
|  {
 | |
|      return RBOOL(ruby_enable_autocompact);
 | |
|  }
 | |
| +#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 42ad028530..411d5eab69 100644
 | |
| --- a/test/ruby/test_gc_compact.rb
 | |
| +++ b/test/ruby/test_gc_compact.rb
 | |
| @@ -9,14 +9,7 @@
 | |
|  end
 | |
|  
 | |
|  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)
 | |
|  
 | |
| @@ -30,10 +23,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
 | |
| @@ -87,13 +89,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
 |