diff options
author | Alan Wu <[email protected]> | 2024-12-19 12:28:21 -0500 |
---|---|---|
committer | GitHub <[email protected]> | 2024-12-19 12:28:21 -0500 |
commit | 5978f2f114a5a669a39cdc46c457a3ea8ee24056 (patch) | |
tree | cd2616b9f860b04b2b3165a7f81eafd9cc9a2684 /imemo.c | |
parent | ce849d565bf6aae8e0179fffb04eb1f665f17347 (diff) |
Fix use-after-free in vm_ccs_free()
`struct rb_callcache *` point to an imemo object on the GC heap when
pushed into `struct rb_class_cc_entries`, but by the time vm_ccs_free()
runs, the entire GC page the imemo was on could already be deallocated.
With the right conditions, vm_ccs_free() wrote to freed memory.
rb_objspace_garbage_object_p() by itself is not enough to determine
liveness.
I conjectured this situation to be possible in
<https://github.com/ruby/ruby/pull/11995> using hints from crashes
in the wild. With c37bdfa5311be0aa8503b995299fb9547cede0a6 ("Make
asan_poison_object poison the whole slot"), the in-tree test suite
now recreates this scenario[^1][^2][^3].
Use rb_gc_pointer_to_heap_p(). Other uses of
rb_objspace_garbage_object_p() could be making the same mistake, but
correcting them might introduce serious performance regressions, so
leave them alone for now.
[^1]: http://ci.rvm.jp/results/trunk_asan@ruby-sp1/5477412
[^2]: http://ci.rvm.jp/results/trunk_asan@ruby-sp1/5477445
[^3]: http://ci.rvm.jp/results/trunk_asan@ruby-sp1/5477448
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/12401
Merged-By: XrXr
Diffstat (limited to 'imemo.c')
-rw-r--r-- | imemo.c | 3 |
1 files changed, 2 insertions, 1 deletions
@@ -463,7 +463,8 @@ vm_ccs_free(struct rb_class_cc_entries *ccs, int alive, VALUE klass) if (!alive) { void *ptr = asan_unpoison_object_temporary((VALUE)cc); // ccs can be free'ed. - if (!rb_objspace_garbage_object_p((VALUE)cc) && + if (rb_gc_pointer_to_heap_p((VALUE)cc) && + !rb_objspace_garbage_object_p((VALUE)cc) && IMEMO_TYPE_P(cc, imemo_callcache) && cc->klass == klass) { // OK. maybe target cc. |