[ruby-dev:39593] infinite recursive rb_block_call

View: New views
2 Messages — Rating Filter:   Alert me  

[ruby-dev:39593] infinite recursive rb_block_call

by Yusuke ENDOH :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

遠藤です。

[ruby-core:24794] と似たような問題は、rb_block_call でも起きるようです。


$ ./ruby -e '
class C
  include Enumerable
  alias :each :min
end
C.new.min
'
Segmentation fault


min 以外でも Enumerable のメソッドで大抵落ちるみたいです。

rb_funcall_no_recursive を真似て rb_block_call_no_recursive を作って
見ました。[ruby-dev:39592] の相互再帰の問題は同じようにあると思います。

この方針だとほぼすべての rb_block_call の呼び出しを書き換えて戻り値が
Qundef でないかどうかチェックする必要がありそうです。可読性や性能を
犠牲にしてでも、このような異常なプログラムを救済すべきでしょうか
(Qundef を返さず直接例外を投げればもう少しすっきりする?)


Index: enum.c
===================================================================
--- enum.c (revision 25576)
+++ enum.c (working copy)
@@ -1101,7 +1101,10 @@
  rb_block_call(obj, id_each, 0, 0, min_ii, (VALUE)result);
     }
     else {
- rb_block_call(obj, id_each, 0, 0, min_i, (VALUE)result);
+ VALUE ret = rb_block_call_no_recursive(obj, id_each, 0, 0, min_i,
(VALUE)result, enum_min);
+ if (ret == Qundef) {
+    rb_raise(rb_eRuntimeError, "recursive call to Enumerable#min");
+ }
     }
     if (result[0] == Qundef) return Qnil;
     return result[0];
Index: vm_eval.c
===================================================================
--- vm_eval.c (revision 25576)
+++ vm_eval.c (working copy)
@@ -815,7 +815,48 @@
     return rb_iterate(iterate_method, (VALUE)&arg, bl_proc, data2);
 }

+struct iter_method_arg_no_recursive {
+    struct iter_method_arg arg;
+    VALUE (*func)();
+};
+
+static VALUE
+iterate_method_no_recursive(VALUE obj)
+{
+    const struct iter_method_arg_no_recursive * arg =
+      (struct iter_method_arg_no_recursive *) obj;
+    rb_method_entry_t *me = rb_search_method_emtry(arg->arg.obj, arg->arg.mid);
+    rb_thread_t *th = GET_THREAD();
+    int call_status;
+
+    if (!me) return Qundef;
+    if (me->def && me->def->type == VM_METHOD_TYPE_CFUNC &&
+ me->def->body.cfunc.func == arg->func)
+ return Qundef;
+    call_status = rb_method_call_status(th, me, CALL_FCALL, Qundef);
+    if (call_status != NOEX_OK) {
+ return Qundef;
+    }
+    stack_check();
+    iterate_method((VALUE) &arg->arg);
+}
+
 VALUE
+rb_block_call_no_recursive(VALUE obj, ID mid, int argc, VALUE * argv,
+   VALUE (*bl_proc) (ANYARGS), VALUE data2,
+   VALUE (*func)())
+{
+    struct iter_method_arg_no_recursive arg;
+
+    arg.arg.obj = obj;
+    arg.arg.mid = mid;
+    arg.arg.argc = argc;
+    arg.arg.argv = argv;
+    arg.func = func;
+    return rb_iterate(iterate_method_no_recursive, (VALUE)&arg,
bl_proc, data2);
+}
+
+VALUE
 rb_each(VALUE obj)
 {
     return rb_call(obj, idEach, 0, 0, CALL_FCALL);

--
Yusuke ENDOH <mame@...>


[ruby-dev:39597] Re: infinite recursive rb_block_call

by Yukihiro Matsumoto :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

まつもと ゆきひろです

In message "Re: [ruby-dev:39593] infinite recursive rb_block_call"
    on Fri, 30 Oct 2009 22:17:04 +0900, Yusuke ENDOH <mame@...> writes:

|この方針だとほぼすべての rb_block_call の呼び出しを書き換えて戻り値が
|Qundef でないかどうかチェックする必要がありそうです。。可読性や性能を
|犠牲にしてでも、このような異常なプログラムを救済すべきでしょうか
|(Qundef を返さず直接例外を投げればもう少しすっきりする?)

その価値はないと思います。