treble37
3/24/2018 - 6:51 PM

Example of the kinds of issues you can hit when you directly mess with memory

Example of the kinds of issues you can hit when you directly mess with memory

require 'fiddle'
public def class=(new_class)
  obj_ptr       = Fiddle::Pointer.new 2*self.object_id
  klass_ptr     = Fiddle::Pointer.new 2*new_class.object_id
  obj_ptr[8, 8] = klass_ptr.ref[0, 8]
end

# here, we set the class to a non-class value. When we call a method on it, it
# tries to treat "false" as if it was a class, but it isn't. This causes invalid
# memory access, which causes a segmentation fault
o = Object.new
o.class = false
o.inspect

# !> program.rb:13: [BUG] Segmentation fault at 0x0000000000000018
# !> ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17]
# !> 
# !> -- Crash Report log information --------------------------------------------
# !>    See Crash Report log file under the one of following:
# !>      * ~/Library/Logs/DiagnosticReports
# !>      * /Library/Logs/DiagnosticReports
# !>    for more details.
# !> Don't forget to include the above Crash Report log file in bug reports.
# !> 
# !> -- Control frame information -----------------------------------------------
# !> c:0002 p:0046 s:0008 E:000f28 EVAL   program.rb:13 [FINISH]
# !> c:0001 p:0000 s:0003 E:000470 (none) [FINISH]
# !> 
# !> -- Ruby level backtrace information ----------------------------------------
# !> program.rb:13:in `<main>'
# !> 
# !> -- Machine register context ------------------------------------------------
# !>  rax: 0x0000000000000000 rbx: 0x0000000000000000 rcx: 0x00000000000002a1
# !>  rdx: 0x00007ffeef173bc0 rdi: 0x0000000000000000 rsi: 0x0000000000000000
# !>  rbp: 0x00007ffeef173bb0 rsp: 0x00007ffeef173b70  r8: 0x00007fa4140f4600
# !>   r9: 0x000000010112b0a0 r10: 0x00007fa4150571f0 r11: 0x00000000000000c0
# !>  r12: 0x0000000000000000 r13: 0x0000000000000aa1 r14: 0x0000000000000aa1
# !>  r15: 0x0000000000000aa1 rip: 0x0000000100c5d335 rfl: 0x0000000000010202
# !> 
# !> -- C level backtrace information -------------------------------------------
# !> 0   ruby                                0x0000000100c77db7 rb_vm_bugreport + 135
# !> 1   ruby                                0x0000000100afced8 rb_bug_context + 472
# !> 2   ruby                                0x0000000100bec3e1 sigsegv + 81
# !> 3   libsystem_platform.dylib            0x00007fff73f70f5a _sigtramp + 26
# !> 4   ruby                                0x0000000100c5d335 method_entry_get + 261
# !> 5   ruby                                0x0000000100c5d3cb rb_callable_method_entry + 27
# !> 6   ruby                                0x0000000100c549bb vm_exec_core + 12123
# !> 7   ruby                                0x0000000100c65b6e vm_exec + 142
# !> 8   ruby                                0x0000000100b069b1 ruby_exec_internal + 177
# !> 9   ruby                                0x0000000100b068a8 ruby_run_node + 56
# !> 10  ruby                                0x0000000100a8c0ff main + 79
# !> 
# !> -- Other runtime information -----------------------------------------------
# !> 
# !> * Loaded script: program.rb
# !> 
# !> * Loaded features:
# !> 
# !>     0 enumerator.so
# !>     1 thread.rb
# !>     2 rational.so
# !>     3 complex.so
# !>     4 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/x86_64-darwin17/enc/encdb.bundle
# !>     5 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/x86_64-darwin17/enc/trans/transdb.bundle
# !>     6 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/x86_64-darwin17/rbconfig.rb
# !>     7 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/compatibility.rb
# !>     8 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/defaults.rb
# !>     9 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/deprecate.rb
# !>    10 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/errors.rb
# !>    11 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/version.rb
# !>    12 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/requirement.rb
# !>    13 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/platform.rb
# !>    14 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/basic_specification.rb
# !>    15 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/stub_specification.rb
# !>    16 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/util/list.rb
# !>    17 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/x86_64-darwin17/stringio.bundle
# !>    18 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/specification.rb
# !>    19 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/exceptions.rb
# !>    20 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/dependency.rb
# !>    21 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/core_ext/kernel_gem.rb
# !>    22 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/monitor.rb
# !>    23 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb
# !>    24 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems.rb
# !>    25 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/rubygems/path_support.rb
# !>    26 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/version.rb
# !>    27 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/core_ext/name_error.rb
# !>    28 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/levenshtein.rb
# !>    29 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/jaro_winkler.rb
# !>    30 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checker.rb
# !>    31 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/delegate.rb
# !>    32 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
# !>    33 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
# !>    34 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/name_error_checkers.rb
# !>    35 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/method_name_checker.rb
# !>    36 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/key_error_checker.rb
# !>    37 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/null_checker.rb
# !>    38 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/formatters/plain_formatter.rb
# !>    39 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean.rb
# !>    40 /Users/xjxc322/.gem/ruby/2.5.0/gems/seeing_is_believing-3.4.0/lib/seeing_is_believing/safe.rb
# !>    41 /Users/xjxc322/.gem/ruby/2.5.0/gems/seeing_is_believing-3.4.0/lib/seeing_is_believing/version.rb
# !>    42 /Users/xjxc322/.gem/ruby/2.5.0/gems/seeing_is_believing-3.4.0/lib/seeing_is_believing/hash_struct.rb
# !>    43 /Users/xjxc322/.gem/ruby/2.5.0/gems/seeing_is_believing-3.4.0/lib/seeing_is_believing/event_stream/events.rb
# !>    44 /Users/xjxc322/.gem/ruby/2.5.0/gems/seeing_is_believing-3.4.0/lib/seeing_is_believing/event_stream/producer.rb
# !>    45 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/x86_64-darwin17/socket.bundle
# !>    46 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/x86_64-darwin17/io/wait.bundle
# !>    47 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/socket.rb
# !>    48 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/timeout.rb
# !>    49 /Users/xjxc322/.gem/ruby/2.5.0/gems/seeing_is_believing-3.4.0/lib/seeing_is_believing/the_matrix.rb
# !>    50 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/prettyprint.rb
# !>    51 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/pp.rb
# !>    52 /Users/xjxc322/.gem/ruby/2.5.0/gems/seeing_is_believing-3.4.0/lib/seeing_is_believing/customize_pp.rb
# !>    53 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/x86_64-darwin17/fiddle.bundle
# !>    54 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/fiddle/function.rb
# !>    55 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/fiddle/closure.rb
# !>    56 /Users/xjxc322/.rubies/ruby-2.5.0/lib/ruby/2.5.0/fiddle.rb
# !> 
# !> [NOTE]
# !> You may have encountered a bug in the Ruby interpreter or extension libraries.
# !> Bug reports are welcome.
# !> For details: http://www.ruby-lang.org/bugreport.html
# !> 
# !> [IMPORTANT]
# !> Don't forget to include the Crash Report log file under
# !> DiagnosticReports directory in bug reports.
# !> 
require 'fiddle'
public def class=(new_class)
  obj_ptr       = Fiddle::Pointer.new 2*self.object_id
  klass_ptr     = Fiddle::Pointer.new 2*new_class.object_id
  obj_ptr[8, 8] = klass_ptr.ref[0, 8]
end

class A
  attr_accessor :a, :b, :c, :d
end

# Instance vars get lost when there are 3 or fewer, due to an optimization
a = A.new
a.a = a.b = a.c = :whatever
a # => #<A:0x00007fd3d68d6880 @c=:whatever, @b=:whatever, @a=:whatever>
a.class = Object
a # => #<Object:0x00007fd3d68d6880>

# Add a fourth ivar and Ruby stores them in the hash table of instance variables
a = A.new
a.a = a.b = a.c = a.d = :whatever
a # => #<A:0x00007fd3d68d62b8 @c=:whatever, @b=:whatever, @a=:whatever, @d=:whatever>
a.class = Object
a # => #<Object:0x00007fd3d68d62b8 @c=:whatever, @b=:whatever, @a=:whatever, @d=:whatever>
require 'fiddle'
public def class=(new_class)
  obj_ptr       = Fiddle::Pointer.new 2*self.object_id
  klass_ptr     = Fiddle::Pointer.new 2*new_class.object_id
  obj_ptr[8, 8] = klass_ptr.ref[0, 8]
end

class A
  attr_accessor :a, :b, :c
end
class B
  attr_accessor :a, :b, :c
end

# The optimization is to store them inside the object instead of allocating the ivar hash
# which ivar corresponds to which location is based on the order they are set in.
# Since we set B's ivars in a different order, their values get swapped.
a   = A.new
a.a = 'a'
a.b = 'b'
a.c = 'c'

b   = B.new
b.c = 'c'
b.a = 'a'
b.b = 'b'

a # => #<A:0x00007fc62797e2b8 @a="a", @b="b", @c="c">
a.class = B
a # => #<B:0x00007fc62797e2b8 @c="a", @a="b", @b="c">