Commit 28eb6271 authored by KOBAYASHI Shuji's avatar KOBAYASHI Shuji

Fix heap-buffer-overflow for small `Hash` (HT) in `Hash#rehash`

### Example

##### example.rb

```ruby
h = {}
(1..17).each{h[_1] = _1}
(1..16).each{h.delete(_1)}
h.rehash
```

##### ASAN report

```console
$ bin/mruby example.rb
==52587==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000006998 at pc 0x55a29cddf96b bp 0x7fff7b1b1720 sp 0x7fff7b1b1710
READ of size 4 at 0x602000006998 thread T0
    #0 0x55a29cddf96a in ib_it_next /mruby/src/hash.c:639
    #1 0x55a29cde2ca2 in ht_rehash /mruby/src/hash.c:900
    #2 0x55a29cde379f in h_rehash /mruby/src/hash.c:996
    #3 0x55a29cde7f3d in mrb_hash_rehash /mruby/src/hash.c:1735
    #4 0x55a29ce77b62 in mrb_vm_exec /mruby/src/vm.c:1451
    #5 0x55a29ce5fa88 in mrb_vm_run /mruby/src/vm.c:981
    #6 0x55a29ceb87e1 in mrb_top_run /mruby/src/vm.c:2874
    #7 0x55a29cf36bdf in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6805
    #8 0x55a29cf36f25 in mrb_load_detect_file_cxt mrbgems/mruby-compiler/core/parse.y:6848
    #9 0x55a29cdba0a2 in main /mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:347
    #10 0x7f24ef43b0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #11 0x55a29cdb4a6d in _start (/mruby/bin/mruby+0x2a3a6d)

0x602000006998 is located 0 bytes to the right of 8-byte region [0x602000006990,0x602000006998)
allocated by thread T0 here:
    #0 0x7f24f01cfffe in __interceptor_realloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dffe)
    #1 0x55a29ceb9440 in mrb_default_allocf /mruby/src/state.c:68
    #2 0x55a29cdba747 in mrb_realloc_simple /mruby/src/gc.c:228
    #3 0x55a29cdba928 in mrb_realloc /mruby/src/gc.c:242
    #4 0x55a29cde12e5 in ht_init /mruby/src/hash.c:749
    #5 0x55a29cde2b8e in ht_rehash /mruby/src/hash.c:897
    #6 0x55a29cde379f in h_rehash /mruby/src/hash.c:996
    #7 0x55a29cde7f3d in mrb_hash_rehash /mruby/src/hash.c:1735
    #8 0x55a29ce77b62 in mrb_vm_exec /mruby/src/vm.c:1451
    #9 0x55a29ce5fa88 in mrb_vm_run /mruby/src/vm.c:981
    #10 0x55a29ceb87e1 in mrb_top_run /mruby/src/vm.c:2874
    #11 0x55a29cf36bdf in mrb_load_exec mrbgems/mruby-compiler/core/parse.y:6805
    #12 0x55a29cf36f25 in mrb_load_detect_file_cxt mrbgems/mruby-compiler/core/parse.y:6848
    #13 0x55a29cdba0a2 in main /mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:347
    #14 0x7f24ef43b0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
```
parent 20342714
...@@ -718,6 +718,7 @@ ib_bit_for(uint32_t size) ...@@ -718,6 +718,7 @@ ib_bit_for(uint32_t size)
static uint32_t static uint32_t
ib_byte_size_for(uint32_t ib_bit) ib_byte_size_for(uint32_t ib_bit)
{ {
mrb_assert(IB_INIT_BIT <= ib_bit);
uint32_t ary_size = IB_INIT_BIT == 4 ? uint32_t ary_size = IB_INIT_BIT == 4 ?
ib_bit_to_capa(ib_bit) * 2 / IB_TYPE_BIT * ib_bit / 2 : ib_bit_to_capa(ib_bit) * 2 / IB_TYPE_BIT * ib_bit / 2 :
ib_bit_to_capa(ib_bit) / IB_TYPE_BIT * ib_bit; ib_bit_to_capa(ib_bit) / IB_TYPE_BIT * ib_bit;
...@@ -892,7 +893,13 @@ static void ...@@ -892,7 +893,13 @@ static void
ht_rehash(mrb_state *mrb, struct RHash *h) ht_rehash(mrb_state *mrb, struct RHash *h)
{ {
/* see comments in `h_rehash` */ /* see comments in `h_rehash` */
uint32_t size = ht_size(h), w_size = 0, ea_capa = ht_ea_capa(h); uint32_t size = ht_size(h);
if (size <= AR_MAX_SIZE) {
ht_to_ar(mrb, h);
ar_rehash(mrb, h);
return;
}
uint32_t w_size = 0, ea_capa = ht_ea_capa(h);
hash_entry *ea = ht_ea(h); hash_entry *ea = ht_ea(h);
ht_init(mrb, h, 0, ea, ea_capa, h_ht(h), ib_bit_for(size)); ht_init(mrb, h, 0, ea, ea_capa, h_ht(h), ib_bit_for(size));
ht_set_size(h, size); ht_set_size(h, size);
......
...@@ -944,6 +944,14 @@ assert('Hash#rehash') do ...@@ -944,6 +944,14 @@ assert('Hash#rehash') do
h = {} h = {}
assert_same(h, h.rehash) assert_same(h, h.rehash)
assert_predicate(h, :empty?) assert_predicate(h, :empty?)
h = {}
(1..17).each{h[_1] = _1 * 2}
(2..16).each{h.delete(_1)}
assert_same(h, h.rehash)
assert_equal([[1, 2], [17, 34]], h.to_a)
assert_equal(2, h.size)
[1, 17].each{assert_equal(_1 * 2, h[_1])}
end end
assert('#eql? receiver should be specified key') do assert('#eql? receiver should be specified key') do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment