summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFrancois Chagnon <[email protected]>2016-11-17 14:52:52 -0500
committerBouke van der Bijl <[email protected]>2016-11-24 10:51:29 -0500
commita384bcce350acf5e8be5d45f0258e6ef5bdeb033 (patch)
tree6b46993ba35c686b2a82a479ddde19c10097f7f1
parenta630c4f413f6af764e68210430e8b61a435d38d7 (diff)
downloadmruby-a384bcce350acf5e8be5d45f0258e6ef5bdeb033.tar.gz
mruby-a384bcce350acf5e8be5d45f0258e6ef5bdeb033.zip
Fix instances where return value of mrb_method_search_vm is unchecked
Reported by @charliesome
-rw-r--r--src/vm.c47
-rw-r--r--test/t/nomethoderror.rb31
2 files changed, 68 insertions, 10 deletions
diff --git a/src/vm.c b/src/vm.c
index a7418e6e7..fed622b85 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -54,6 +54,10 @@ The value below allows about 60000 recursive calls in the simplest case. */
#define ARENA_RESTORE(mrb,ai) (mrb)->gc.arena_idx = (ai)
+#define CALL_MAXARGS 127
+
+void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
+
static inline void
stack_clear(mrb_value *from, size_t count)
{
@@ -362,9 +366,13 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
c = mrb_class(mrb, self);
p = mrb_method_search_vm(mrb, &c, mid);
if (!p) {
+ mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+ p = mrb_method_search_vm(mrb, &c, missing);
+ if (!p) {
+ mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
+ mrb_method_missing(mrb, mid, self, args);
+ }
undef = mid;
- mid = mrb_intern_lit(mrb, "method_missing");
- p = mrb_method_search_vm(mrb, &c, mid);
n++; argc++;
}
ci = cipush(mrb);
@@ -749,10 +757,6 @@ argnum_error(mrb_state *mrb, mrb_int num)
#endif
-#define CALL_MAXARGS 127
-
-void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
-
MRB_API mrb_value
mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
{
@@ -1290,8 +1294,20 @@ RETRY_TRY_BLOCK:
c = mrb->c->ci->target_class->super;
m = mrb_method_search_vm(mrb, &c, mid);
if (!m) {
- mid = mrb_intern_lit(mrb, "method_missing");
- m = mrb_method_search_vm(mrb, &c, mid);
+ mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+ m = mrb_method_search_vm(mrb, &c, missing);
+ if (!m) {
+ mrb_value args;
+
+ if (n == CALL_MAXARGS) {
+ args = regs[a+1];
+ }
+ else {
+ args = mrb_ary_new_from_values(mrb, n, regs+a+1);
+ }
+ mrb_method_missing(mrb, mid, recv, args);
+ }
+ mid = missing;
if (n == CALL_MAXARGS) {
mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid));
}
@@ -1681,9 +1697,20 @@ RETRY_TRY_BLOCK:
m = mrb_method_search_vm(mrb, &c, mid);
if (!m) {
mrb_value sym = mrb_symbol_value(mid);
+ mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
+ m = mrb_method_search_vm(mrb, &c, missing);
+ if (!m) {
+ mrb_value args;
- mid = mrb_intern_lit(mrb, "method_missing");
- m = mrb_method_search_vm(mrb, &c, mid);
+ if (n == CALL_MAXARGS) {
+ args = regs[a+1];
+ }
+ else {
+ args = mrb_ary_new_from_values(mrb, n, regs+a+1);
+ }
+ mrb_method_missing(mrb, mid, recv, args);
+ }
+ mid = missing;
if (n == CALL_MAXARGS) {
mrb_ary_unshift(mrb, regs[a+1], sym);
}
diff --git a/test/t/nomethoderror.rb b/test/t/nomethoderror.rb
index 5fed79689..ce3514782 100644
--- a/test/t/nomethoderror.rb
+++ b/test/t/nomethoderror.rb
@@ -20,3 +20,34 @@ assert('NoMethodError#args', '15.2.32.2.1') do
end
end
end
+
+assert('Can still raise when BasicObject#method_missing is removed') do
+ assert_raise(TypeError) do
+ begin
+ BasicObject.alias_method(:old_method_missing, :method_missing)
+ BasicObject.remove_method(:method_missing)
+ 1.__send__(:foo)
+ ensure
+ BasicObject.alias_method(:method_missing, :old_method_missing)
+ BasicObject.remove_method(:old_method_missing)
+ end
+ end
+end
+
+assert('Can still call super when BasicObject#method_missing is removed') do
+ assert_raise(TypeError) do
+ class A
+ def foo
+ super
+ end
+ end
+ begin
+ BasicObject.alias_method(:old_method_missing, :method_missing)
+ BasicObject.remove_method(:method_missing)
+ A.new.foo
+ ensure
+ BasicObject.alias_method(:method_missing, :old_method_missing)
+ BasicObject.remove_method(:old_method_missing)
+ end
+ end
+end