Skip to content

Commit 12102d1

Browse files
committed
Fix crash in WeakMap during compaction
WeakMap can crash during compaction because the st_insert could allocate memory.
1 parent 746eede commit 12102d1

4 files changed

Lines changed: 32 additions & 14 deletions

File tree

gc.c

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9799,15 +9799,6 @@ gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj)
97999799
return FALSE;
98009800
}
98019801

9802-
/* Used in places that could malloc, which can cause the GC to run. We need to
9803-
* temporarily disable the GC to allow the malloc to happen. */
9804-
#define COULD_MALLOC_REGION_START() \
9805-
GC_ASSERT(during_gc); \
9806-
VALUE _already_disabled = rb_gc_disable_no_rest(); \
9807-
9808-
#define COULD_MALLOC_REGION_END() \
9809-
if (_already_disabled == Qfalse) rb_objspace_gc_enable(objspace);
9810-
98119802
static VALUE
98129803
gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, size_t slot_size)
98139804
{
@@ -9840,11 +9831,11 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
98409831

98419832
if (FL_TEST((VALUE)src, FL_EXIVAR)) {
98429833
/* Resizing the st table could cause a malloc */
9843-
COULD_MALLOC_REGION_START();
9834+
DURING_GC_COULD_MALLOC_REGION_START();
98449835
{
98459836
rb_mv_generic_ivar((VALUE)src, (VALUE)dest);
98469837
}
9847-
COULD_MALLOC_REGION_END();
9838+
DURING_GC_COULD_MALLOC_REGION_END();
98489839
}
98499840

98509841
st_data_t srcid = (st_data_t)src, id;
@@ -9854,12 +9845,12 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t src_slot_size, s
98549845
if (st_lookup(objspace->obj_to_id_tbl, srcid, &id)) {
98559846
gc_report(4, objspace, "Moving object with seen id: %p -> %p\n", (void *)src, (void *)dest);
98569847
/* Resizing the st table could cause a malloc */
9857-
COULD_MALLOC_REGION_START();
9848+
DURING_GC_COULD_MALLOC_REGION_START();
98589849
{
98599850
st_delete(objspace->obj_to_id_tbl, &srcid, 0);
98609851
st_insert(objspace->obj_to_id_tbl, (st_data_t)dest, id);
98619852
}
9862-
COULD_MALLOC_REGION_END();
9853+
DURING_GC_COULD_MALLOC_REGION_END();
98639854
}
98649855

98659856
/* Move the object */

internal/gc.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,17 @@ struct rb_objspace; /* in vm_core.h */
189189
# define SIZE_POOL_COUNT 5
190190
#endif
191191

192+
/* Used in places that could malloc during, which can cause the GC to run. We
193+
* need to temporarily disable the GC to allow the malloc to happen.
194+
* Allocating memory during GC is a bad idea, so use this only when absolutely
195+
* necessary. */
196+
#define DURING_GC_COULD_MALLOC_REGION_START() \
197+
assert(rb_during_gc()); \
198+
VALUE _already_disabled = rb_gc_disable_no_rest()
199+
200+
#define DURING_GC_COULD_MALLOC_REGION_END() \
201+
if (_already_disabled == Qfalse) rb_gc_enable()
202+
192203
typedef struct ractor_newobj_size_pool_cache {
193204
struct RVALUE *freelist;
194205
struct heap_page *using_page;

test/ruby/test_weakmap.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,18 @@ def test_compaction
223223
224224
assert_equal(val, wm[key])
225225
end;
226+
227+
assert_separately(["-W0"], <<-'end;')
228+
wm = ObjectSpace::WeakMap.new
229+
230+
ary = 10_000.times.map do
231+
o = Object.new
232+
wm[o] = 1
233+
o
234+
end
235+
236+
GC.verify_compaction_references(expand_heap: true, toward: :empty)
237+
end;
226238
end
227239

228240
def test_replaced_values_bug_19531

weakmap.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,11 @@ wmap_compact_table_i(st_data_t key, st_data_t val, st_data_t data)
122122
if (key_obj != new_key_obj) {
123123
*(VALUE *)key = new_key_obj;
124124

125-
st_insert(table, key, val);
125+
DURING_GC_COULD_MALLOC_REGION_START();
126+
{
127+
st_insert(table, key, val);
128+
}
129+
DURING_GC_COULD_MALLOC_REGION_END();
126130

127131
return ST_DELETE;
128132
}

0 commit comments

Comments
 (0)