Skip to content

Instantly share code, notes, and snippets.

@shuujii
Last active November 13, 2020 05:03
mruby Hash bugs (everything has been fixed in mruby master)

Hash#rehash does not reindex completely

Example

def new_modified_key_hash
  k = [:a]
  h = {k=>1, [:b]=>2}
  k[0] = :b
  h  #=> {[:b]=>1, [:b]=>2}
end

h = new_modified_key_hash
h.rehash
p h  #=>   Actual: {[:b]=>1, [:b]=>2}
     #   Expected: {[:b]=>2}

h = new_modified_key_hash
3.upto(17){h[_1] = _1}
h.rehash
p h  #=>   Actual: {[:b]=>1, [:b]=>2, 3=>3, 4=>4, ... 16=>16, 17=>17}
     #   Expected: {[:b]=>2, 3=>3, 4=>4, ... 16=>16, 17=>17}

Heap use-after-free in Hash#value?

Example

class A
  def ==(o)
    $h.clear
    super
  end
end

$h = {a: 1, b: 2}
$h.value?(A.new)

ASAN report

==8505==ERROR: AddressSanitizer: heap-use-after-free on address 0x60d000000930 at pc 0x55ecc9d52b2a bp 0x7ffd49f8e450 sp 0x7ffd49f8e440
READ of size 2 at 0x60d000000930 thread T0
    #0 0x55ecc9d52b29 in ht_foreach /home/shuujii/mruby/mruby/src/hash.c:513
    #1 0x55ecc9d5a19b in mrb_hash_has_value /home/shuujii/mruby/mruby/src/hash.c:1353
    #2 0x55ecc9e1dbe6 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:1402
    #3 0x55ecc9e072b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #4 0x55ecc9e895a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #5 0x55ecc9f05160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #6 0x55ecc9f052e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #7 0x55ecc9d29af3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #8 0x7f5c322ce0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #9 0x55ecc9d24a4d in _start (/home/shuujii/mruby/mruby/build/host.baseline/bin/mruby+0x38ca4d)

0x60d000000930 is located 0 bytes inside of 144-byte region [0x60d000000930,0x60d0000009c0)
freed by thread T0 here:
    #0 0x7f5c330627cf in __interceptor_free (/lib/x86_64-linux-gnu/libasan.so.5+0x10d7cf)
    #1 0x55ecc9e89f65 in mrb_default_allocf /home/shuujii/mruby/mruby/src/state.c:64
    #2 0x55ecc9d2a64a in mrb_free /home/shuujii/mruby/mruby/src/gc.c:273
    #3 0x55ecc9d5378f in ht_free /home/shuujii/mruby/mruby/src/hash.c:586
    #4 0x55ecc9d58bf7 in mrb_hash_clear /home/shuujii/mruby/mruby/src/hash.c:1114
    #5 0x55ecc9e1dbe6 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:1402
    #6 0x55ecc9e072b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #7 0x55ecc9e8910c in mrb_run /home/shuujii/mruby/mruby/src/vm.c:2759
    #8 0x55ecc9e00b56 in mrb_funcall_with_block /home/shuujii/mruby/mruby/src/vm.c:521
    #9 0x55ecc9e00d05 in mrb_funcall_argv /home/shuujii/mruby/mruby/src/vm.c:532
    #10 0x55ecc9dfe3aa in mrb_funcall /home/shuujii/mruby/mruby/src/vm.c:401
    #11 0x55ecc9eb0c6f in mrb_equal /home/shuujii/mruby/mruby/src/object.c:61
    #12 0x55ecc9d59f0c in hash_has_value_i /home/shuujii/mruby/mruby/src/hash.c:1323
    #13 0x55ecc9d52aa1 in ht_foreach /home/shuujii/mruby/mruby/src/hash.c:519
    #14 0x55ecc9d5a19b in mrb_hash_has_value /home/shuujii/mruby/mruby/src/hash.c:1353
    #15 0x55ecc9e1dbe6 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:1402
    #16 0x55ecc9e072b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #17 0x55ecc9e895a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #18 0x55ecc9f05160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #19 0x55ecc9f052e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #20 0x55ecc9d29af3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #21 0x7f5c322ce0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

previously allocated by thread T0 here:
    #0 0x7f5c33062ffe in __interceptor_realloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dffe)
    #1 0x55ecc9e89f7f in mrb_default_allocf /home/shuujii/mruby/mruby/src/state.c:68
    #2 0x55ecc9d2a0e1 in mrb_realloc_simple /home/shuujii/mruby/mruby/src/gc.c:211
    #3 0x55ecc9d2a2c2 in mrb_realloc /home/shuujii/mruby/mruby/src/gc.c:225
    #4 0x55ecc9d2a449 in mrb_malloc /home/shuujii/mruby/mruby/src/gc.c:241
    #5 0x55ecc9d4e9e9 in segment_alloc /home/shuujii/mruby/mruby/src/hash.c:296
    #6 0x55ecc9d50d01 in ht_put /home/shuujii/mruby/mruby/src/hash.c:403
    #7 0x55ecc9d55aa9 in mrb_hash_set /home/shuujii/mruby/mruby/src/hash.c:758
    #8 0x55ecc9e4da82 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:2501
    #9 0x55ecc9e072b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #10 0x55ecc9e895a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #11 0x55ecc9f05160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #12 0x55ecc9f052e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #13 0x55ecc9d29af3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #14 0x7f5c322ce0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: heap-use-after-free /home/shuujii/mruby/mruby/src/hash.c:513 in ht_foreach
Shadow bytes around the buggy address:
  0x0c1a7fff80d0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c1a7fff80e0: 00 00 00 00 00 00 00 00 00 02 fa fa fa fa fa fa
  0x0c1a7fff80f0: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c1a7fff8100: 00 00 00 02 fa fa fa fa fa fa fa fa 00 00 00 00
  0x0c1a7fff8110: 00 00 00 00 00 00 00 00 00 00 00 00 00 04 fa fa
=>0x0c1a7fff8120: fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd fd fd
  0x0c1a7fff8130: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
  0x0c1a7fff8140: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1a7fff8150: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1a7fff8160: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1a7fff8170: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==8505==ABORTING

Heap use-after-free in ht_hash_equal

Example

class A
  def eql?(o)
    $h.clear
    super
  end
end

$h = {a: 1}
$h[A.new]

ASAN report

==8382==ERROR: AddressSanitizer: heap-use-after-free on address 0x60d000000930 at pc 0x5649c5910e67 bp 0x7fff7ae91310 sp 0x7fff7ae91300
READ of size 2 at 0x60d000000930 thread T0
    #0 0x5649c5910e66 in ht_get /home/shuujii/mruby/mruby/src/hash.c:455
    #1 0x5649c5914525 in mrb_hash_get /home/shuujii/mruby/mruby/src/hash.c:728
    #2 0x5649c591557f in mrb_hash_aget /home/shuujii/mruby/mruby/src/hash.c:851
    #3 0x5649c59dcbe6 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:1402
    #4 0x5649c59c62b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #5 0x5649c5a485a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #6 0x5649c5ac4160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #7 0x5649c5ac42e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #8 0x5649c58e8af3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #9 0x7f4e505c70b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #10 0x5649c58e3a4d in _start (/home/shuujii/mruby/mruby/build/host.baseline/bin/mruby+0x38ca4d)

0x60d000000930 is located 0 bytes inside of 144-byte region [0x60d000000930,0x60d0000009c0)
freed by thread T0 here:
    #0 0x7f4e5135b7cf in __interceptor_free (/lib/x86_64-linux-gnu/libasan.so.5+0x10d7cf)
    #1 0x5649c5a48f65 in mrb_default_allocf /home/shuujii/mruby/mruby/src/state.c:64
    #2 0x5649c58e964a in mrb_free /home/shuujii/mruby/mruby/src/gc.c:273
    #3 0x5649c591278f in ht_free /home/shuujii/mruby/mruby/src/hash.c:586
    #4 0x5649c5917bf7 in mrb_hash_clear /home/shuujii/mruby/mruby/src/hash.c:1114
    #5 0x5649c59dcbe6 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:1402
    #6 0x5649c59c62b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #7 0x5649c5a4810c in mrb_run /home/shuujii/mruby/mruby/src/vm.c:2759
    #8 0x5649c59bfb56 in mrb_funcall_with_block /home/shuujii/mruby/mruby/src/vm.c:521
    #9 0x5649c59bfd05 in mrb_funcall_argv /home/shuujii/mruby/mruby/src/vm.c:532
    #10 0x5649c59bd3aa in mrb_funcall /home/shuujii/mruby/mruby/src/vm.c:401
    #11 0x5649c5a73542 in mrb_eql /home/shuujii/mruby/mruby/src/object.c:658
    #12 0x5649c590b6eb in ht_hash_equal /home/shuujii/mruby/mruby/src/hash.c:126
    #13 0x5649c5910cb3 in ht_get /home/shuujii/mruby/mruby/src/hash.c:462
    #14 0x5649c5914525 in mrb_hash_get /home/shuujii/mruby/mruby/src/hash.c:728
    #15 0x5649c591557f in mrb_hash_aget /home/shuujii/mruby/mruby/src/hash.c:851
    #16 0x5649c59dcbe6 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:1402
    #17 0x5649c59c62b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #18 0x5649c5a485a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #19 0x5649c5ac4160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #20 0x5649c5ac42e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #21 0x5649c58e8af3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #22 0x7f4e505c70b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

previously allocated by thread T0 here:
    #0 0x7f4e5135bffe in __interceptor_realloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dffe)
    #1 0x5649c5a48f7f in mrb_default_allocf /home/shuujii/mruby/mruby/src/state.c:68
    #2 0x5649c58e90e1 in mrb_realloc_simple /home/shuujii/mruby/mruby/src/gc.c:211
    #3 0x5649c58e92c2 in mrb_realloc /home/shuujii/mruby/mruby/src/gc.c:225
    #4 0x5649c58e9449 in mrb_malloc /home/shuujii/mruby/mruby/src/gc.c:241
    #5 0x5649c590d9e9 in segment_alloc /home/shuujii/mruby/mruby/src/hash.c:296
    #6 0x5649c590fd01 in ht_put /home/shuujii/mruby/mruby/src/hash.c:403
    #7 0x5649c5914aa9 in mrb_hash_set /home/shuujii/mruby/mruby/src/hash.c:758
    #8 0x5649c5a0ca82 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:2501
    #9 0x5649c59c62b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #10 0x5649c5a485a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #11 0x5649c5ac4160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #12 0x5649c5ac42e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #13 0x5649c58e8af3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #14 0x7f4e505c70b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: heap-use-after-free /home/shuujii/mruby/mruby/src/hash.c:455 in ht_get
Shadow bytes around the buggy address:
  0x0c1a7fff80d0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c1a7fff80e0: 00 00 00 00 00 00 00 00 00 02 fa fa fa fa fa fa
  0x0c1a7fff80f0: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c1a7fff8100: 00 00 00 02 fa fa fa fa fa fa fa fa 00 00 00 00
  0x0c1a7fff8110: 00 00 00 00 00 00 00 00 00 00 00 00 00 04 fa fa
=>0x0c1a7fff8120: fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd fd fd
  0x0c1a7fff8130: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
  0x0c1a7fff8140: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1a7fff8150: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1a7fff8160: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1a7fff8170: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==8382==ABORTING

Heap use-after-free in ht_hash_func

Example

class A
  def hash
    $h.clear
    1
  end
end

$h = {}
(1..17).each{|n| $h[n] = n}
$h[A.new]

ASAN report

==8455==ERROR: AddressSanitizer: heap-use-after-free on address 0x604000001f30 at pc 0x55642cc4cfc5 bp 0x7ffe7cbfaba0 sp 0x7ffe7cbfab90
READ of size 8 at 0x604000001f30 thread T0
    #0 0x55642cc4cfc4 in ht_hash_func /home/shuujii/mruby/mruby/src/hash.c:79
    #1 0x55642cc524ae in ht_index_get /home/shuujii/mruby/mruby/src/hash.c:427
    #2 0x55642cc52a1c in ht_get /home/shuujii/mruby/mruby/src/hash.c:450
    #3 0x55642cc56525 in mrb_hash_get /home/shuujii/mruby/mruby/src/hash.c:728
    #4 0x55642cc5757f in mrb_hash_aget /home/shuujii/mruby/mruby/src/hash.c:851
    #5 0x55642cd1ebe6 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:1402
    #6 0x55642cd082b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #7 0x55642cd8a5a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #8 0x55642ce06160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #9 0x55642ce062e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #10 0x55642cc2aaf3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #11 0x7f9ffe8840b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #12 0x55642cc25a4d in _start (/home/shuujii/mruby/mruby/build/host.baseline/bin/mruby+0x38ca4d)

0x604000001f30 is located 32 bytes inside of 40-byte region [0x604000001f10,0x604000001f38)
freed by thread T0 here:
    #0 0x7f9fff6187cf in __interceptor_free (/lib/x86_64-linux-gnu/libasan.so.5+0x10d7cf)
    #1 0x55642cd8af65 in mrb_default_allocf /home/shuujii/mruby/mruby/src/state.c:64
    #2 0x55642cc2b64a in mrb_free /home/shuujii/mruby/mruby/src/gc.c:273
    #3 0x55642cc54882 in ht_free /home/shuujii/mruby/mruby/src/hash.c:589
    #4 0x55642cc59bf7 in mrb_hash_clear /home/shuujii/mruby/mruby/src/hash.c:1114
    #5 0x55642cd1ebe6 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:1402
    #6 0x55642cd082b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #7 0x55642cd8a10c in mrb_run /home/shuujii/mruby/mruby/src/vm.c:2759
    #8 0x55642cd01b56 in mrb_funcall_with_block /home/shuujii/mruby/mruby/src/vm.c:521
    #9 0x55642cd01d05 in mrb_funcall_argv /home/shuujii/mruby/mruby/src/vm.c:532
    #10 0x55642ccff3aa in mrb_funcall /home/shuujii/mruby/mruby/src/vm.c:401
    #11 0x55642cc4cf1f in ht_hash_func /home/shuujii/mruby/mruby/src/hash.c:75
    #12 0x55642cc524ae in ht_index_get /home/shuujii/mruby/mruby/src/hash.c:427
    #13 0x55642cc52a1c in ht_get /home/shuujii/mruby/mruby/src/hash.c:450
    #14 0x55642cc56525 in mrb_hash_get /home/shuujii/mruby/mruby/src/hash.c:728
    #15 0x55642cc5757f in mrb_hash_aget /home/shuujii/mruby/mruby/src/hash.c:851
    #16 0x55642cd1ebe6 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:1402
    #17 0x55642cd082b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #18 0x55642cd8a5a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #19 0x55642ce06160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #20 0x55642ce062e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #21 0x55642cc2aaf3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #22 0x7f9ffe8840b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

previously allocated by thread T0 here:
    #0 0x7f9fff618ffe in __interceptor_realloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dffe)
    #1 0x55642cd8af7f in mrb_default_allocf /home/shuujii/mruby/mruby/src/state.c:68
    #2 0x55642cc2b0e1 in mrb_realloc_simple /home/shuujii/mruby/mruby/src/gc.c:211
    #3 0x55642cc2b2c2 in mrb_realloc /home/shuujii/mruby/mruby/src/gc.c:225
    #4 0x55642cc2b449 in mrb_malloc /home/shuujii/mruby/mruby/src/gc.c:241
    #5 0x55642cc4d8e3 in ht_new /home/shuujii/mruby/mruby/src/hash.c:141
    #6 0x55642cc55321 in mrb_hash_new_capa /home/shuujii/mruby/mruby/src/hash.c:651
    #7 0x55642cd4e728 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:2496
    #8 0x55642cd082b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #9 0x55642cd8a5a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #10 0x55642ce06160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #11 0x55642ce062e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #12 0x55642cc2aaf3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #13 0x7f9ffe8840b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: heap-use-after-free /home/shuujii/mruby/mruby/src/hash.c:79 in ht_hash_func
Shadow bytes around the buggy address:
  0x0c087fff8390: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
  0x0c087fff83a0: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
  0x0c087fff83b0: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
  0x0c087fff83c0: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 02
  0x0c087fff83d0: fa fa 00 00 00 00 00 04 fa fa 00 00 00 00 00 07
=>0x0c087fff83e0: fa fa fd fd fd fd[fd]fa fa fa fa fa fa fa fa fa
  0x0c087fff83f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8410: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8420: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8430: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==8455==ABORTING

Heap use-after-free in mrb_hash_merge

Example

class A
  def eql?(o)
    $h.clear
    super
  end
end

$h = {A.new => 1}
{a: 0, **$h}

ASAN report

==8548==ERROR: AddressSanitizer: heap-use-after-free on address 0x60d000000930 at pc 0x56071cff5b2a bp 0x7ffd299c5c00 sp 0x7ffd299c5bf0
READ of size 2 at 0x60d000000930 thread T0
    #0 0x56071cff5b29 in ht_foreach /home/shuujii/mruby/mruby/src/hash.c:513
    #1 0x56071cffd621 in mrb_hash_merge /home/shuujii/mruby/mruby/src/hash.c:1381
    #2 0x56071d0f1ae0 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:2523
    #3 0x56071d0aa2b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #4 0x56071d12c5a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #5 0x56071d1a8160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #6 0x56071d1a82e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #7 0x56071cfccaf3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #8 0x7f78c28530b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #9 0x56071cfc7a4d in _start (/home/shuujii/mruby/mruby/build/host.baseline/bin/mruby+0x38ca4d)

0x60d000000930 is located 0 bytes inside of 144-byte region [0x60d000000930,0x60d0000009c0)
freed by thread T0 here:
    #0 0x7f78c35e77cf in __interceptor_free (/lib/x86_64-linux-gnu/libasan.so.5+0x10d7cf)
    #1 0x56071d12cf65 in mrb_default_allocf /home/shuujii/mruby/mruby/src/state.c:64
    #2 0x56071cfcd64a in mrb_free /home/shuujii/mruby/mruby/src/gc.c:273
    #3 0x56071cff678f in ht_free /home/shuujii/mruby/mruby/src/hash.c:586
    #4 0x56071cffbbf7 in mrb_hash_clear /home/shuujii/mruby/mruby/src/hash.c:1114
    #5 0x56071d0c0be6 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:1402
    #6 0x56071d0aa2b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #7 0x56071d12c10c in mrb_run /home/shuujii/mruby/mruby/src/vm.c:2759
    #8 0x56071d0a3b56 in mrb_funcall_with_block /home/shuujii/mruby/mruby/src/vm.c:521
    #9 0x56071d0a3d05 in mrb_funcall_argv /home/shuujii/mruby/mruby/src/vm.c:532
    #10 0x56071d0a13aa in mrb_funcall /home/shuujii/mruby/mruby/src/vm.c:401
    #11 0x56071d157542 in mrb_eql /home/shuujii/mruby/mruby/src/object.c:658
    #12 0x56071cfef6eb in ht_hash_equal /home/shuujii/mruby/mruby/src/hash.c:126
    #13 0x56071cff367d in ht_put /home/shuujii/mruby/mruby/src/hash.c:381
    #14 0x56071cffd2c9 in merge_i /home/shuujii/mruby/mruby/src/hash.c:1362
    #15 0x56071cff5aa1 in ht_foreach /home/shuujii/mruby/mruby/src/hash.c:519
    #16 0x56071cffd621 in mrb_hash_merge /home/shuujii/mruby/mruby/src/hash.c:1381
    #17 0x56071d0f1ae0 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:2523
    #18 0x56071d0aa2b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #19 0x56071d12c5a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #20 0x56071d1a8160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #21 0x56071d1a82e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #22 0x56071cfccaf3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #23 0x7f78c28530b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

previously allocated by thread T0 here:
    #0 0x7f78c35e7ffe in __interceptor_realloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dffe)
    #1 0x56071d12cf7f in mrb_default_allocf /home/shuujii/mruby/mruby/src/state.c:68
    #2 0x56071cfcd0e1 in mrb_realloc_simple /home/shuujii/mruby/mruby/src/gc.c:211
    #3 0x56071cfcd2c2 in mrb_realloc /home/shuujii/mruby/mruby/src/gc.c:225
    #4 0x56071cfcd449 in mrb_malloc /home/shuujii/mruby/mruby/src/gc.c:241
    #5 0x56071cff19e9 in segment_alloc /home/shuujii/mruby/mruby/src/hash.c:296
    #6 0x56071cff3d01 in ht_put /home/shuujii/mruby/mruby/src/hash.c:403
    #7 0x56071cff8aa9 in mrb_hash_set /home/shuujii/mruby/mruby/src/hash.c:758
    #8 0x56071d0f0a82 in mrb_vm_exec /home/shuujii/mruby/mruby/src/vm.c:2501
    #9 0x56071d0aa2b3 in mrb_vm_run /home/shuujii/mruby/mruby/src/vm.c:918
    #10 0x56071d12c5a1 in mrb_top_run /home/shuujii/mruby/mruby/src/vm.c:2773
    #11 0x56071d1a8160 in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6522
    #12 0x56071d1a82e7 in mrb_load_file_cxt mrbgems/mruby-compiler/core/parse.y:6531
    #13 0x56071cfccaf3 in main /home/shuujii/mruby/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:337
    #14 0x7f78c28530b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: heap-use-after-free /home/shuujii/mruby/mruby/src/hash.c:513 in ht_foreach
Shadow bytes around the buggy address:
  0x0c1a7fff80d0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c1a7fff80e0: 00 00 00 00 00 00 00 00 00 02 fa fa fa fa fa fa
  0x0c1a7fff80f0: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c1a7fff8100: 00 00 00 02 fa fa fa fa fa fa fa fa 00 00 00 00
  0x0c1a7fff8110: 00 00 00 00 00 00 00 00 00 00 00 00 00 04 fa fa
=>0x0c1a7fff8120: fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd fd fd
  0x0c1a7fff8130: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
  0x0c1a7fff8140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c1a7fff8150: 00 00 fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1a7fff8160: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1a7fff8170: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==8548==ABORTING

Repeated deletes and inserts increase memory usage of Hash

Example

require 'open3'

LIB_MEMUSAGE = "/usr/lib/x86_64-linux-gnu/libmemusage.so"
MRUBY = ENV["MRUBY"] || "bin/mruby"
SIZE = 20
TIMES = 10000

def mem(code)
  env = {"LD_PRELOAD" => LIB_MEMUSAGE}
  code = "GC.start\n#{code}"
  args = %W[#{MRUBY} -e #{code}]
  out, err, status = Open3.capture3(env, *args)
  if status.success?
    err[/heap peak: \d+/]
  else
    print out, err
    puts
    cmdline = "#{env.map{|*kv| kv * '=' } * ' '} #{args * ' '}"
    raise "command faild(#{status.exitstatus}): #{cmdline}"
  end
end

def upto(from, to, counter_var, code)
  <<-EOS
    #{counter_var} = #{from}
    while #{counter_var} <= #{to}
      #{code}
      #{counter_var} += 1
    end
  EOS
end

print mem <<-EOS
  h = {}
  #{upto 1, SIZE, :n, "h[n] = n"}
EOS
puts " (hash size = #{SIZE}, standard)"

print mem <<-EOS
  h = {}
  #{upto 1, SIZE, :n, "h[n] = n"}
  #{upto 1, TIMES, :n, "h.delete(1); h[1] = 1"}
EOS
puts " (hash size = #{SIZE}, #{TIMES} iterations of deletion/insertion)"

Output

heap peak: 211958 (hash size = 20, standard)
heap peak: 369549 (hash size = 20, 10000 iterations of deletion/insertion)

The lower row should have approximately the same value as the upper row.

Self-replacement does not work for Hash#replace

Example

h = {a: 1}
p h.replace(h)  #=>   Actual: {}
                #   Expected: {a: 1}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment