Skip to content

Commit eb92159

Browse files
committed
Revert ruby#2486
This reverts commits: 10d6a3a 8ba48c1 fba8627 dd883de 6c6a25f 167e6b4 7cb96d4 3207979 595b3c4 1521f7c c11c5e6 cf33608 3632a81 f56506b 86427a3 . The reason for the revert is that we observe ABA problem around inline method cache. When a cache misshits, we search for a method entry. And if the entry is identical to what was cached before, we reuse the cache. But the commits we are reverting here introduced situations where a method entry is freed, then the identical memory region is used for another method entry. An inline method cache cannot detect that ABA. Here is a code that reproduce such situation: ```ruby require 'prime' class << Integer alias org_sqrt sqrt def sqrt(n) raise end GC.stress = true Prime.each(7*37){} rescue nil # <- Here we populate CC class << Object.new; end # These adjacent remove-then-alias maneuver # frees a method entry, then immediately # reuses it for another. remove_method :sqrt alias sqrt org_sqrt end Prime.each(7*37).to_a # <- SEGV ```
1 parent ef69738 commit eb92159

14 files changed

+391
-501
lines changed

class.c

+12-33
Original file line numberDiff line numberDiff line change
@@ -956,41 +956,25 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super)
956956
return method_changed;
957957
}
958958

959-
typedef struct tuple {
960-
struct RClass *klass;
961-
struct RClass *origin;
962-
} tuple;
963-
964-
static enum rb_id_table_iterator_result
965-
inject_refined_method(ID *key, VALUE *value, void *data, int _)
966-
{
967-
const tuple *ptr = data;
968-
const rb_method_entry_t *me = *(const rb_method_entry_t **) value;
969-
const rb_method_entry_t *orig_me = me->def->body.refined.orig_me;
970-
const rb_method_entry_t *new_me =
971-
rb_method_entry_from_template(
972-
me, &(rb_method_refined_t) {
973-
.orig_me = NULL,
974-
.owner = me->def->body.refined.owner, });
975-
rb_id_table_insert(RCLASS_M_TBL(ptr->klass), *key, (VALUE)new_me);
976-
RB_OBJ_WRITTEN(ptr->klass, Qundef, new_me);
977-
*value = (VALUE)rb_method_entry_clone(orig_me);
978-
RB_OBJ_WRITTEN(ptr->origin, Qundef, orig_me);
979-
return ID_TABLE_CONTINUE;
980-
}
981-
982959
static enum rb_id_table_iterator_result
983960
move_refined_method(ID key, VALUE value, void *data)
984961
{
985-
const tuple *ptr = data;
986-
const rb_method_entry_t *me = (const rb_method_entry_t *) value;
962+
rb_method_entry_t *me = (rb_method_entry_t *) value;
963+
VALUE klass = (VALUE)data;
964+
struct rb_id_table *tbl = RCLASS_M_TBL(klass);
987965

988966
if (me->def->type == VM_METHOD_TYPE_REFINED) {
989967
if (me->def->body.refined.orig_me) {
990-
return ID_TABLE_REPLACE;
968+
const rb_method_entry_t *orig_me = me->def->body.refined.orig_me, *new_me;
969+
RB_OBJ_WRITE(me, &me->def->body.refined.orig_me, NULL);
970+
new_me = rb_method_entry_clone(me);
971+
rb_id_table_insert(tbl, key, (VALUE)new_me);
972+
RB_OBJ_WRITTEN(klass, Qundef, new_me);
973+
rb_method_entry_copy(me, orig_me);
974+
return ID_TABLE_CONTINUE;
991975
}
992976
else {
993-
rb_id_table_insert(RCLASS_M_TBL(ptr->klass), key, (VALUE)me);
977+
rb_id_table_insert(tbl, key, (VALUE)me);
994978
return ID_TABLE_DELETE;
995979
}
996980
}
@@ -1016,12 +1000,7 @@ rb_prepend_module(VALUE klass, VALUE module)
10161000
RCLASS_SET_ORIGIN(klass, origin);
10171001
RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
10181002
RCLASS_M_TBL_INIT(klass);
1019-
rb_id_table_foreach_with_replace_with_key(
1020-
RCLASS_M_TBL(origin),
1021-
move_refined_method,
1022-
inject_refined_method,
1023-
&(tuple) { RCLASS(klass), RCLASS(origin), },
1024-
true);
1003+
rb_id_table_foreach(RCLASS_M_TBL(origin), move_refined_method, (void *)klass);
10251004
}
10261005
changed = include_modules_at(klass, klass, module, FALSE);
10271006
if (changed < 0)

ext/coverage/coverage.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ method_coverage_i(void *vstart, void *vend, size_t stride, void *data)
123123

124124
for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) {
125125
if (RB_TYPE_P(v, T_IMEMO) && imemo_type(v) == imemo_ment) {
126-
const rb_method_entry_t *me = (const rb_method_entry_t *) v;
126+
const rb_method_entry_t *me = (rb_method_entry_t *) v;
127127
VALUE path, first_lineno, first_column, last_lineno, last_column;
128128
VALUE data[5], ncoverage, methods;
129129
VALUE methods_id = ID2SYM(rb_intern("methods"));

gc.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -7829,9 +7829,9 @@ void rb_update_st_references(struct st_table *ht)
78297829
}
78307830

78317831
static void
7832-
gc_ref_update_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
7832+
gc_ref_update_method_entry(rb_objspace_t *objspace, rb_method_entry_t *me)
78337833
{
7834-
const rb_method_definition_t *def = me->def;
7834+
rb_method_definition_t *def = me->def;
78357835

78367836
UPDATE_IF_MOVED(objspace, me->owner);
78377837
UPDATE_IF_MOVED(objspace, me->defined_class);

id_table.c

+40-45
Original file line numberDiff line numberDiff line change
@@ -269,62 +269,57 @@ rb_id_table_delete(struct rb_id_table *tbl, ID id)
269269
void
270270
rb_id_table_foreach_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, rb_id_table_update_callback_func_t *replace, void *data)
271271
{
272-
rb_id_table_foreach_with_replace_with_key(tbl, func, replace, data, false);
272+
int i, capa = tbl->capa;
273+
274+
for (i=0; i<capa; i++) {
275+
if (ITEM_KEY_ISSET(tbl, i)) {
276+
const id_key_t key = ITEM_GET_KEY(tbl, i);
277+
enum rb_id_table_iterator_result ret = (*func)(Qundef, tbl->items[i].val, data);
278+
assert(key != 0);
279+
280+
if (ret == ID_TABLE_REPLACE) {
281+
VALUE val = tbl->items[i].val;
282+
ret = (*replace)(NULL, &val, data, TRUE);
283+
tbl->items[i].val = val;
284+
}
285+
else if (ret == ID_TABLE_STOP)
286+
return;
287+
}
288+
}
273289
}
274290

275291
void
276292
rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data)
277293
{
278-
rb_id_table_foreach_with_replace_with_key(tbl, func, 0, data, true);
279-
}
280-
281-
typedef struct tuple {
282-
rb_id_table_foreach_values_func_t *const func;
283-
void *const data;
284-
} tuple;
294+
int i, capa = tbl->capa;
285295

286-
static enum rb_id_table_iterator_result
287-
cdr(ID car, VALUE cdr, void *data)
288-
{
289-
const tuple *ptr = data;
290-
return ptr->func(cdr, ptr->data);
296+
for (i=0; i<capa; i++) {
297+
if (ITEM_KEY_ISSET(tbl, i)) {
298+
const id_key_t key = ITEM_GET_KEY(tbl, i);
299+
enum rb_id_table_iterator_result ret = (*func)(key2id(key), tbl->items[i].val, data);
300+
assert(key != 0);
301+
302+
if (ret == ID_TABLE_DELETE)
303+
hash_delete_index(tbl, i);
304+
else if (ret == ID_TABLE_STOP)
305+
return;
306+
}
307+
}
291308
}
292309

293310
void
294311
rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data)
295312
{
296-
rb_id_table_foreach_with_replace(
297-
tbl, cdr, 0, &(tuple) { func, data, });
298-
}
313+
int i, capa = tbl->capa;
299314

300-
void
301-
rb_id_table_foreach_with_replace_with_key(
302-
struct rb_id_table *tbl,
303-
rb_id_table_foreach_func_t *func,
304-
rb_id_table_update_callback_func_t *replace,
305-
void *data,
306-
bool needkey)
307-
{
308-
for (int i = 0; i < tbl->capa; i++) {
309-
if (ITEM_KEY_ISSET(tbl, i)) {
310-
const id_key_t key = ITEM_GET_KEY(tbl, i);
311-
assert(key != 0);
312-
ID k = needkey ? key2id(key) : 0;
313-
VALUE v = tbl->items[i].val;
314-
switch (func(k, v, data)) {
315-
case ID_TABLE_DELETE:
316-
hash_delete_index(tbl, i);
317-
/* FALLTHROUGH */
318-
case ID_TABLE_CONTINUE:
319-
continue;
320-
case ID_TABLE_STOP:
321-
return;
322-
case ID_TABLE_REPLACE:
323-
if (replace) {
324-
replace(&k, &v, data, true);
325-
tbl->items[i].val = v;
326-
}
327-
}
328-
}
315+
for (i=0; i<capa; i++) {
316+
if (ITEM_KEY_ISSET(tbl, i)) {
317+
enum rb_id_table_iterator_result ret = (*func)(tbl->items[i].val, data);
318+
319+
if (ret == ID_TABLE_DELETE)
320+
hash_delete_index(tbl, i);
321+
else if (ret == ID_TABLE_STOP)
322+
return;
323+
}
329324
}
330325
}

id_table.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ enum rb_id_table_iterator_result {
1010
ID_TABLE_STOP = ST_STOP,
1111
ID_TABLE_DELETE = ST_DELETE,
1212
ID_TABLE_REPLACE = ST_REPLACE,
13+
ID_TABLE_ITERATOR_RESULT_END
1314
};
1415

1516
struct rb_id_table *rb_id_table_create(size_t size);
@@ -28,7 +29,6 @@ typedef enum rb_id_table_iterator_result rb_id_table_foreach_func_t(ID id, VALUE
2829
typedef enum rb_id_table_iterator_result rb_id_table_foreach_values_func_t(VALUE val, void *data);
2930
void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data);
3031
void rb_id_table_foreach_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, rb_id_table_update_callback_func_t *replace, void *data);
31-
void rb_id_table_foreach_with_replace_with_key(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, rb_id_table_update_callback_func_t *replace, void *data, bool needkey);
3232
void rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data);
3333

3434
#endif /* RUBY_ID_TABLE_H */

insns.def

+1-1
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,7 @@ invokeblock
911911
// attr rb_snum_t sp_inc = sp_inc_of_invokeblock(ci);
912912
{
913913
static struct rb_call_cache cc = {
914-
0, 0, NULL, vm_invokeblock_i,
914+
0, 0, NULL, NULL, vm_invokeblock_i,
915915
};
916916

917917
VALUE bh = VM_BLOCK_HANDLER_NONE;

internal.h

+2
Original file line numberDiff line numberDiff line change
@@ -2330,6 +2330,7 @@ enum method_missing_reason {
23302330
MISSING_NONE = 0x40
23312331
};
23322332
struct rb_callable_method_entry_struct;
2333+
struct rb_method_definition_struct;
23332334
struct rb_execution_context_struct;
23342335
struct rb_control_frame_struct;
23352336
struct rb_calling_info;
@@ -2341,6 +2342,7 @@ struct rb_call_cache {
23412342

23422343
/* inline cache: values */
23432344
const struct rb_callable_method_entry_struct *me;
2345+
const struct rb_method_definition_struct *def;
23442346

23452347
VALUE (*call)(struct rb_execution_context_struct *ec,
23462348
struct rb_control_frame_struct *cfp,

method.h

+60-29
Original file line numberDiff line numberDiff line change
@@ -49,23 +49,54 @@ typedef struct rb_cref_struct {
4949
/* method data type */
5050

5151
typedef struct rb_method_entry_struct {
52-
const VALUE flags;
53-
const VALUE defined_class;
52+
VALUE flags;
53+
VALUE defined_class;
5454
struct rb_method_definition_struct * const def;
55-
const ID called_id;
56-
const VALUE owner;
55+
ID called_id;
56+
VALUE owner;
5757
} rb_method_entry_t;
5858

5959
typedef struct rb_callable_method_entry_struct { /* same fields with rb_method_entry_t */
60-
const VALUE flags;
60+
VALUE flags;
6161
const VALUE defined_class;
6262
struct rb_method_definition_struct * const def;
63-
const ID called_id;
63+
ID called_id;
6464
const VALUE owner;
6565
} rb_callable_method_entry_t;
6666

6767
#define METHOD_ENTRY_VISI(me) (rb_method_visibility_t)(((me)->flags & (IMEMO_FL_USER0 | IMEMO_FL_USER1)) >> (IMEMO_FL_USHIFT+0))
6868
#define METHOD_ENTRY_BASIC(me) (int) (((me)->flags & (IMEMO_FL_USER2 )) >> (IMEMO_FL_USHIFT+2))
69+
#define METHOD_ENTRY_COMPLEMENTED(me) ((me)->flags & IMEMO_FL_USER3)
70+
#define METHOD_ENTRY_COMPLEMENTED_SET(me) ((me)->flags = (me)->flags | IMEMO_FL_USER3)
71+
72+
static inline void
73+
METHOD_ENTRY_VISI_SET(rb_method_entry_t *me, rb_method_visibility_t visi)
74+
{
75+
VM_ASSERT((int)visi >= 0 && visi <= 3);
76+
me->flags = (me->flags & ~(IMEMO_FL_USER0 | IMEMO_FL_USER1)) | (visi << (IMEMO_FL_USHIFT+0));
77+
}
78+
static inline void
79+
METHOD_ENTRY_BASIC_SET(rb_method_entry_t *me, unsigned int basic)
80+
{
81+
VM_ASSERT(basic <= 1);
82+
me->flags = (me->flags & ~(IMEMO_FL_USER2 )) | (basic << (IMEMO_FL_USHIFT+2));
83+
}
84+
static inline void
85+
METHOD_ENTRY_FLAGS_SET(rb_method_entry_t *me, rb_method_visibility_t visi, unsigned int basic)
86+
{
87+
VM_ASSERT((int)visi >= 0 && visi <= 3);
88+
VM_ASSERT(basic <= 1);
89+
me->flags =
90+
(me->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)) |
91+
((visi << (IMEMO_FL_USHIFT+0)) | (basic << (IMEMO_FL_USHIFT+2)));
92+
}
93+
static inline void
94+
METHOD_ENTRY_FLAGS_COPY(rb_method_entry_t *dst, const rb_method_entry_t *src)
95+
{
96+
dst->flags =
97+
(dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)) |
98+
(src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2));
99+
}
69100

70101
typedef enum {
71102
VM_METHOD_TYPE_ISEQ, /*!< Ruby method */
@@ -93,32 +124,32 @@ typedef struct rb_iseq_struct rb_iseq_t;
93124
#endif
94125

95126
typedef struct rb_method_iseq_struct {
96-
const rb_iseq_t *const iseqptr; /*!< iseq pointer, should be separated from iseqval */
97-
rb_cref_t *const cref; /*!< class reference, should be marked */
98-
} rb_method_iseq_t;
127+
rb_iseq_t * iseqptr; /*!< iseq pointer, should be separated from iseqval */
128+
rb_cref_t * cref; /*!< class reference, should be marked */
129+
} rb_method_iseq_t; /* check rb_add_method_iseq() when modify the fields */
99130

100131
typedef struct rb_method_cfunc_struct {
101-
VALUE (*const func)(ANYARGS);
102-
VALUE (*const invoker)(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS));
103-
const int argc;
132+
VALUE (*func)(ANYARGS);
133+
VALUE (*invoker)(VALUE recv, int argc, const VALUE *argv, VALUE (*func)(ANYARGS));
134+
int argc;
104135
} rb_method_cfunc_t;
105136

106137
typedef struct rb_method_attr_struct {
107-
const ID id;
108-
const VALUE location; /* should be marked */
138+
ID id;
139+
VALUE location; /* should be marked */
109140
} rb_method_attr_t;
110141

111142
typedef struct rb_method_alias_struct {
112-
const struct rb_method_entry_struct *const original_me; /* original_me->klass is original owner */
143+
struct rb_method_entry_struct * original_me; /* original_me->klass is original owner */
113144
} rb_method_alias_t;
114145

115146
typedef struct rb_method_refined_struct {
116-
const struct rb_method_entry_struct *const orig_me;
117-
const VALUE owner;
147+
struct rb_method_entry_struct * orig_me;
148+
VALUE owner;
118149
} rb_method_refined_t;
119150

120151
typedef struct rb_method_bmethod_struct {
121-
const VALUE proc; /* should be marked */
152+
VALUE proc; /* should be marked */
122153
struct rb_hook_list_struct *hooks;
123154
} rb_method_bmethod_t;
124155

@@ -130,22 +161,22 @@ enum method_optimized_type {
130161
};
131162

132163
struct rb_method_definition_struct {
133-
BITFIELD(rb_method_type_t, const type, VM_METHOD_TYPE_MINIMUM_BITS);
164+
BITFIELD(rb_method_type_t, type, VM_METHOD_TYPE_MINIMUM_BITS);
134165
int alias_count : 28;
135166
int complemented_count : 28;
136167

137168
union {
138-
const rb_method_iseq_t iseq;
139-
const rb_method_cfunc_t cfunc;
140-
const rb_method_attr_t attr;
141-
const rb_method_alias_t alias;
142-
const rb_method_refined_t refined;
169+
rb_method_iseq_t iseq;
170+
rb_method_cfunc_t cfunc;
171+
rb_method_attr_t attr;
172+
rb_method_alias_t alias;
173+
rb_method_refined_t refined;
143174
rb_method_bmethod_t bmethod;
144175

145-
const enum method_optimized_type optimize_type;
176+
enum method_optimized_type optimize_type;
146177
} body;
147178

148-
const ID original_id;
179+
ID original_id;
149180
};
150181

151182
typedef struct rb_method_definition_struct rb_method_definition_t;
@@ -161,9 +192,8 @@ void rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *c
161192
void rb_add_refined_method_entry(VALUE refined_class, ID mid);
162193
void rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi);
163194

164-
const rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_visibility_t noex);
165-
const rb_method_entry_t *rb_method_entry_from_template(const rb_method_entry_t *template, const void *opts);
166-
const rb_method_entry_t *rb_method_entry_for_missing(ID mid, VALUE klass);
195+
rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_visibility_t noex);
196+
rb_method_entry_t *rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, const rb_method_definition_t *def);
167197

168198
const rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
169199

@@ -193,6 +223,7 @@ void rb_sweep_method_entry(void *vm);
193223

194224
const rb_method_entry_t *rb_method_entry_clone(const rb_method_entry_t *me);
195225
const rb_callable_method_entry_t *rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID called_id, VALUE defined_class);
226+
void rb_method_entry_copy(rb_method_entry_t *dst, const rb_method_entry_t *src);
196227

197228
void rb_scope_visibility_set(rb_method_visibility_t);
198229

0 commit comments

Comments
 (0)