diff options
| author | KOBAYASHI Shuji <[email protected]> | 2019-12-25 20:01:55 +0900 |
|---|---|---|
| committer | KOBAYASHI Shuji <[email protected]> | 2019-12-25 21:31:10 +0900 |
| commit | 0b2d54f4f1d66a0b00625dcc634966d67c29b8b8 (patch) | |
| tree | fb34f7702c2654160b4a34b639b147df2a116d91 | |
| parent | d4cbe6271cc8343647fd0841bb549e1b7c804c34 (diff) | |
| download | mruby-0b2d54f4f1d66a0b00625dcc634966d67c29b8b8.tar.gz mruby-0b2d54f4f1d66a0b00625dcc634966d67c29b8b8.zip | |
Fix potentially use of wrong method cache
#### Example (with `MRB_METHOD_CACHE`)
```ruby
GC.start
c = Class.new
p c #=> #<Class:0x7fd6a180e790>
c.new #=> cache `c.new`
c = nil
GC.start #=> `c` is GCed
r = Range.dup
p r #=> #<Class:0x7fd6a180e790>
# [same pointer as `c`]
r.new(2, 3) #=> ArgumentError: 'initialize':
# wrong number of arguments (2 for 0)
# [`c.new` is called instead of `r.new`]
```
#### Cause
An entry of method cache is identified by class pointer and method
id. However, reusing memory after GC may create a class with the same
pointer as the cached class.
#### Treatment
Cleared method caches of the class when the class is GCed.
| -rw-r--r-- | include/mruby/class.h | 6 | ||||
| -rw-r--r-- | src/class.c | 10 | ||||
| -rw-r--r-- | src/gc.c | 2 |
3 files changed, 12 insertions, 6 deletions
diff --git a/include/mruby/class.h b/include/mruby/class.h index c15633e83..cbf96fef2 100644 --- a/include/mruby/class.h +++ b/include/mruby/class.h @@ -95,6 +95,12 @@ void mrb_gc_mark_mt(mrb_state*, struct RClass*); size_t mrb_gc_mark_mt_size(mrb_state*, struct RClass*); void mrb_gc_free_mt(mrb_state*, struct RClass*); +#ifdef MRB_METHOD_CACHE +void mrb_mc_clear_by_class(mrb_state *mrb, struct RClass* c); +#else +#define mrb_mc_clear_by_class(mrb,c) +#endif + MRB_END_DECL #endif /* MRUBY_CLASS_H */ diff --git a/src/class.c b/src/class.c index 0da572b0e..700bf5485 100644 --- a/src/class.c +++ b/src/class.c @@ -285,11 +285,9 @@ mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super) static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value); #ifdef MRB_METHOD_CACHE static void mc_clear_all(mrb_state *mrb); -static void mc_clear_by_class(mrb_state *mrb, struct RClass*); static void mc_clear_by_id(mrb_state *mrb, struct RClass*, mrb_sym); #else #define mc_clear_all(mrb) -#define mc_clear_by_class(mrb,c) #define mc_clear_by_id(mrb,c,s) #endif @@ -303,7 +301,7 @@ mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass) super = mrb->object_class; super->flags |= MRB_FL_CLASS_IS_INHERITED; s = mrb_obj_value(super); - mc_clear_by_class(mrb, klass); + mrb_mc_clear_by_class(mrb, klass); mid = mrb_intern_lit(mrb, "inherited"); if (!mrb_func_basic_p(mrb, s, mid, mrb_bob_init)) { mrb_value c = mrb_obj_value(klass); @@ -1109,7 +1107,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru m->flags |= MRB_FL_CLASS_IS_INHERITED; ins_pos->super = ic; mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); - mc_clear_by_class(mrb, ins_pos); + mrb_mc_clear_by_class(mrb, ins_pos); ins_pos = ic; skip: m = m->super; @@ -1322,8 +1320,8 @@ mc_clear_all(mrb_state *mrb) } } -static void -mc_clear_by_class(mrb_state *mrb, struct RClass *c) +void +mrb_mc_clear_by_class(mrb_state *mrb, struct RClass *c) { struct mrb_cache_entry *mc = mrb->cache; int i; @@ -807,10 +807,12 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) case MRB_TT_SCLASS: mrb_gc_free_mt(mrb, (struct RClass*)obj); mrb_gc_free_iv(mrb, (struct RObject*)obj); + mrb_mc_clear_by_class(mrb, (struct RClass*)obj); break; case MRB_TT_ICLASS: if (MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN)) mrb_gc_free_mt(mrb, (struct RClass*)obj); + mrb_mc_clear_by_class(mrb, (struct RClass*)obj); break; case MRB_TT_ENV: { |
