Skip to content

Commit 2ccf6e5

Browse files
committed
Don't allow allocating memory during GC
Allocating memory (xmalloc and xrealloc) during GC could cause GC to trigger, which would crash with `[BUG] during_gc != 0`. This is an intermittent bug which could be hard to debug. This commit changes it so that any memory allocation during GC will emit a warning. When debug flags are enabled it will also cause a crash.
1 parent 3d2eb7c commit 2ccf6e5

File tree

1 file changed

+48
-12
lines changed

1 file changed

+48
-12
lines changed

gc.c

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9902,6 +9902,17 @@ gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj)
99029902
return FALSE;
99039903
}
99049904

9905+
/* Used in places that could malloc, which can cause the GC to run. We need to
9906+
* temporarily disable the GC to allow the malloc to happen. */
9907+
#define COULD_MALLOC_REGION_START() \
9908+
GC_ASSERT(during_gc); \
9909+
VALUE _already_disabled = rb_gc_disable_no_rest(); \
9910+
during_gc = false;
9911+
9912+
#define COULD_MALLOC_REGION_END() \
9913+
during_gc = true; \
9914+
if (_already_disabled == Qfalse) rb_objspace_gc_enable(objspace);
9915+
99059916
static VALUE
99069917
gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, size_t slot_size)
99079918
{
@@ -9930,11 +9941,12 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
99309941
CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS((VALUE)src), (VALUE)src);
99319942

99329943
if (FL_TEST((VALUE)src, FL_EXIVAR)) {
9933-
/* Same deal as below. Generic ivars are held in st tables.
9934-
* Resizing the table could cause a GC to happen and we can't allow it */
9935-
VALUE already_disabled = rb_gc_disable_no_rest();
9936-
rb_mv_generic_ivar((VALUE)src, (VALUE)dest);
9937-
if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace);
9944+
/* Resizing the st table could cause a malloc */
9945+
COULD_MALLOC_REGION_START();
9946+
{
9947+
rb_mv_generic_ivar((VALUE)src, (VALUE)dest);
9948+
}
9949+
COULD_MALLOC_REGION_END();
99389950
}
99399951

99409952
st_data_t srcid = (st_data_t)src, id;
@@ -9943,13 +9955,13 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
99439955
* the object to object id mapping. */
99449956
if (st_lookup(objspace->obj_to_id_tbl, srcid, &id)) {
99459957
gc_report(4, objspace, "Moving object with seen id: %p -> %p\n", (void *)src, (void *)dest);
9946-
/* inserting in the st table can cause the GC to run. We need to
9947-
* prevent re-entry in to the GC since `gc_move` is running in the GC,
9948-
* so temporarily disable the GC around the st table mutation */
9949-
VALUE already_disabled = rb_gc_disable_no_rest();
9950-
st_delete(objspace->obj_to_id_tbl, &srcid, 0);
9951-
st_insert(objspace->obj_to_id_tbl, (st_data_t)dest, id);
9952-
if (already_disabled == Qfalse) rb_objspace_gc_enable(objspace);
9958+
/* Resizing the st table could cause a malloc */
9959+
COULD_MALLOC_REGION_START();
9960+
{
9961+
st_delete(objspace->obj_to_id_tbl, &srcid, 0);
9962+
st_insert(objspace->obj_to_id_tbl, (st_data_t)dest, id);
9963+
}
9964+
COULD_MALLOC_REGION_END();
99539965
}
99549966

99559967
/* Move the object */
@@ -12192,9 +12204,26 @@ objspace_malloc_prepare(rb_objspace_t *objspace, size_t size)
1219212204
return size;
1219312205
}
1219412206

12207+
static bool
12208+
malloc_during_gc_p(rb_objspace_t *objspace)
12209+
{
12210+
/* malloc is not allowed during GC when we're not using multiple ractors
12211+
* (since ractors can run while another thread is sweeping) and when we
12212+
* have the GVL (since if we don't have the GVL, we'll try to acquire the
12213+
* GVL which will block and ensure the other thread finishes GC). */
12214+
return during_gc && !rb_multi_ractor_p() && ruby_thread_has_gvl_p();
12215+
}
12216+
1219512217
static inline void *
1219612218
objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
1219712219
{
12220+
if (UNLIKELY(malloc_during_gc_p(objspace))) {
12221+
rb_warn("malloc during GC detected, this could cause crashes if it triggers another GC");
12222+
#if RGENGC_CHECK_MODE
12223+
rb_bug("Cannot malloc during GC");
12224+
#endif
12225+
}
12226+
1219812227
size = objspace_malloc_size(objspace, mem, size);
1219912228
objspace_malloc_increase(objspace, mem, size, 0, MEMOP_TYPE_MALLOC);
1220012229

@@ -12273,6 +12302,13 @@ xmalloc2_size(const size_t count, const size_t elsize)
1227312302
static void *
1227412303
objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t old_size)
1227512304
{
12305+
if (UNLIKELY(malloc_during_gc_p(objspace))) {
12306+
rb_warn("realloc during GC detected, this could cause crashes if it triggers another GC");
12307+
#if RGENGC_CHECK_MODE
12308+
rb_bug("Cannot realloc during GC");
12309+
#endif
12310+
}
12311+
1227612312
void *mem;
1227712313

1227812314
if (!ptr) return objspace_xmalloc0(objspace, new_size);

0 commit comments

Comments
 (0)