diff options
author | KJ Tsanaktsidis <kj@kjtsanaktsidis.id.au> | 2023-11-27 20:26:17 +1100 |
---|---|---|
committer | KJ Tsanaktsidis <kj@kjtsanaktsidis.id.au> | 2024-01-11 10:44:57 +1100 |
commit | 25f5b83689fc6dd137d45b634a0cd6e8bd024728 (patch) | |
tree | 7ed4dfe986acf7dab851bd9c06531a01d03ca74f /gc.c | |
parent | 5906f6a50ed4c6d3e23595ecf5feea615f0965d5 (diff) |
Fix crash when printing RGENGC_DEBUG=5 output from GC
I was trying to debug an (unrelated) issue in the GC, and wanted to turn
on the trace-level GC output by compiling it with -DRGENGC_DEBUG=5.
Unfortunately, this actually causes a crash in newobj_init() because the
code there tries to log the obj_info() of the newly created object.
However, the object is not actually sufficiently set up for some of the
things that obj_info() tries to do:
* The instance variable table for a class is not yet initialized, and
when using variable-length RVALUES, said ivar table is embedded in
as-yet unitialized memory after the struct RValue. Attempting to read
this, as obj_info() does, causes a crash.
* T_DATA variables need to dereference their ->type field to print out
the underlying C type name, which is not set up until newobj_fill() is
called.
To fix this, create a new method `obj_info_basic`, which dumps out only
the parts of the object that are valid before the object is fully
initialized.
[Fixes #18795]
Diffstat (limited to 'gc.c')
-rw-r--r-- | gc.c | 29 |
1 files changed, 28 insertions, 1 deletions
@@ -1427,6 +1427,7 @@ static inline void gc_prof_set_heap_info(rb_objspace_t *); #endif PRINTF_ARGS(static void gc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...), 3, 4); static const char *obj_info(VALUE obj); +static const char *obj_info_basic(VALUE obj); static const char *obj_type_name(VALUE obj); static void gc_finalize_deferred(void *dmy); @@ -2638,7 +2639,7 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace, GC_ASSERT(!SPECIAL_CONST_P(obj)); /* check alignment */ #endif - gc_report(5, objspace, "newobj: %s\n", obj_info(obj)); + gc_report(5, objspace, "newobj: %s\n", obj_info_basic(obj)); // RUBY_DEBUG_LOG("obj:%p (%s)", (void *)obj, obj_type_name(obj)); return obj; @@ -14099,6 +14100,17 @@ rb_raw_obj_info(char *const buff, const size_t buff_size, VALUE obj) return buff; } +const char * +rb_raw_obj_info_basic(char *const buff, const size_t buff_size, VALUE obj) +{ + asan_unpoisoning_object(obj) { + size_t pos = rb_raw_obj_info_common(buff, buff_size, obj); + if (pos >= buff_size) {} // truncated + } + + return buff; +} + #undef APPEND_S #undef APPEND_F #undef BUFF_ARGS @@ -14130,12 +14142,27 @@ obj_info(VALUE obj) char *const buff = obj_info_buffers[index]; return rb_raw_obj_info(buff, OBJ_INFO_BUFFERS_SIZE, obj); } + +static const char * +obj_info_basic(VALUE obj) +{ + rb_atomic_t index = atomic_inc_wraparound(&obj_info_buffers_index, OBJ_INFO_BUFFERS_NUM); + char *const buff = obj_info_buffers[index]; + return rb_raw_obj_info_basic(buff, OBJ_INFO_BUFFERS_SIZE, obj); +} #else static const char * obj_info(VALUE obj) { return obj_type_name(obj); } + +static const char * +obj_info_basic(VALUE obj) +{ + return obj_type_name(obj); +} + #endif const char * |