Skip to content

Making splat argument objects invisible from Ruby side - #6356

Merged
matz merged 1 commit into
mruby:masterfrom
dearblue:get-args-splat
Sep 20, 2024
Merged

Making splat argument objects invisible from Ruby side#6356
matz merged 1 commit into
mruby:masterfrom
dearblue:get-args-splat

Conversation

@dearblue

Copy link
Copy Markdown
Contributor

The mrb_get_argv() function and the * specifier of mrb_get_args() get the address of the argument. At this time, if it is passed in the form of a splat argument, it will be an address to an element of an array object.

After getting the pointer to the array object, the caller may call mrb_vm_exec() directly or indirectly. At this time, a splat argument with the class set can be retrieved as an array object by searching with ObjectSpace.each_object. If changes are made as array objects, addresses on the heap as arrays may become invalid, or objects in the array may be recycled by the GC. When the caller references the changed address in a subsequent operation, use-after-free is established.

This patch assigns NULL as the class of the array object so that it cannot be detected by ObjectSpace.each_object from the Ruby side.

The `mrb_get_argv()` function and the `*` specifier of `mrb_get_args()` get the address of the argument.
At this time, if it is passed in the form of a splat argument, it will be an address to an element of an array object.

After getting the pointer to the array object, the caller may call `mrb_vm_exec()` directly or indirectly.
At this time, a splat argument with the class set can be retrieved as an array object by searching with `ObjectSpace.each_object`.
If changes are made as array objects, addresses on the heap as arrays may become invalid, or objects in the array may be recycled by the GC.
When the caller references the changed address in a subsequent operation, use-after-free is established.

This patch assigns `NULL` as the class of the array object so that it cannot be detected by `ObjectSpace.each_object` from the Ruby side.
@dearblue
dearblue requested a review from matz as a code owner September 19, 2024 14:22
@github-actions github-actions Bot added the core label Sep 19, 2024
@dearblue

Copy link
Copy Markdown
Contributor Author

Use-after-free with the Hash#values_at method provided by mruby-hash-ext.

$wolf = Object.new

hh = Hash.new { |h, k|
  ObjectSpace.each_object(Array) { |o|
    if o[-1] == $wolf
      o.clear
      GC.start
    end
  }
}

hh.values_at(*%w(sheep1 sheep2 sheep3 sheep4 sheep5), $wolf)
% valgrind -- build/host/bin/mruby uaf-splat1.rb
==93446== Memcheck, a memory error detector
==93446== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==93446== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info
==93446== Command: build/host/bin/mruby uaf-splat1.rb
==93446==
==93446== Invalid read of size 8
==93446==    at 0x2D8614: hash_values_at (hash-ext.c:36)
==93446==    by 0x2952EF: mrb_vm_exec (vm.c:0)
==93446==    by 0x2BA223: mrb_load_exec (parse.y:6912)
==93446==    by 0x2BA543: mrb_load_detect_file_cxt (parse.y:6955)
==93446==    by 0x260A23: main (mruby.c:353)
==93446==  Address 0x54e1d78 is 8 bytes inside a block of size 80 free'd
==93446==    at 0x484F2EC: free (vg_replace_malloc.c:993)
==93446==    by 0x283DD8: mrb_default_allocf (allocf.c:22)
==93446==    by 0x264AF1: mrb_ary_clear (array.c:1265)
==93446==    by 0x264AF1: mrb_ary_clear_m (array.c:1281)
==93446==    by 0x2952EF: mrb_vm_exec (vm.c:0)
==93446==    by 0x290D48: mrb_yield (vm.c:1086)
==93446==    by 0x2D9958: os_each_object_cb (mruby_objectspace.c:149)
==93446==    by 0x27126C: gc_each_objects (gc.c:1506)
==93446==    by 0x27126C: mrb_objspace_each_objects (gc.c:1529)
==93446==    by 0x2D9883: os_each_object (mruby_objectspace.c:173)
==93446==    by 0x2952EF: mrb_vm_exec (vm.c:0)
==93446==    by 0x28F546: mrb_run (vm.c:3058)
==93446==    by 0x28F546: mrb_funcall_with_block (vm.c:750)
==93446==    by 0x28EFD4: mrb_funcall_argv (vm.c:761)
==93446==    by 0x28EFD4: mrb_funcall_id (vm.c:559)
==93446==    by 0x2733F8: hash_default (hash.c:1162)
==93446==    by 0x2733F8: mrb_hash_get (hash.c:1221)
==93446==  Block was alloc'd at
==93446==    at 0x4852321: realloc (vg_replace_malloc.c:1806)
==93446==    by 0x2703A9: mrb_realloc_simple (gc.c:197)
==93446==    by 0x2703A9: mrb_realloc (gc.c:211)
==93446==    by 0x261437: ary_expand_capa (array.c:227)
==93446==    by 0x261ADB: mrb_ary_push (array.c:551)
==93446==    by 0x2A2D13: mrb_vm_exec (vm.c:2666)
==93446==    by 0x2BA223: mrb_load_exec (parse.y:6912)
==93446==    by 0x2BA543: mrb_load_detect_file_cxt (parse.y:6955)
==93446==    by 0x260A23: main (mruby.c:353)
==93446==
==93446==
==93446== HEAP SUMMARY:
==93446==     in use at exit: 0 bytes in 0 blocks
==93446==   total heap usage: 802 allocs, 802 frees, 230,914 bytes allocated
==93446==
==93446== All heap blocks were freed -- no leaks are possible
==93446==
==93446== For lists of detected and suppressed errors, rerun with: -s
==93446== ERROR SUMMARY: 5 errors from 1 contexts (suppressed: 0 from 0)

Similar results can be obtained with mruby-print's mrb_print().

@matz
matz merged commit c162f8f into mruby:master Sep 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants