[#25636] [Oniguruma 3.X] reggnu.c — "K.Kosako" <sndgk393@...>

さっき気がついたのですが、元々は

15 messages 2005/02/05

[#25655] openssl binding for SSL_CTX_set_default_verify_paths and X509_STORE_set_default_paths — Tanaka Akira <akr@...17n.org>

open-uri で https を扱うことを考えていろいろと調べていた所、openssl で、

9 messages 2005/02/08
[#25670] Re: openssl binding for SSL_CTX_set_default_verify_paths and X509_STORE_set_default_paths — GOTOU Yuuzou <gotoyuzo@...> 2005/02/10

In message <876513vce0.fsf@serein.a02.aist.go.jp>,

[#25713] pthread trouble on sighandler — Hidetoshi NAGAI <nagai@...>

永井@知能.九工大です.

17 messages 2005/02/18
[#25714] Re: pthread trouble on sighandler — Yukihiro Matsumoto <matz@...> 2005/02/18

まつもと ゆきひろです

[#25755] I/O operation differs signal handler — Minero Aoki <aamine@...>

青木です。

14 messages 2005/02/24
[#25756] Re: I/O operation differs signal handler — Tanaka Akira <akr@...17n.org> 2005/02/24

In article <20050224091450P.aamine@loveruby.net>,

[ruby-dev:25609] Re: some problems on ext/tk/sample/**/*.rb

From: "H.Yamamoto" <ocean@...2.ccsnet.ne.jp>
Date: 2005-02-01 08:06:30 UTC
List: ruby-dev #25609
山本です。

>問題がありましたらご連絡ください.

あれから調べていたのですが、なぜ namespace が壊れるのか
ようやくわかりました。結論から言うと、Tcl のメニューを
押したとき ruby 側のコールバックが呼ばれますが、そこで
Kernel.exit を呼ぶと、現在の実装では Tcl の環境をまたいで
longjmp が行われてしまいます。そのため、フレームとして
登録されていたスタックな CallFrame が Pop されず残って
しまい、フレームの破壊につながったと考えられます。

// 再現手順

tcltklib.c(ip_ruby_cmd) の 1815行目

  rb_raise(rb_eSystemExit, RSTRING(res)->ptr);

にブレークポイントを置くと、コールスタックは以下のようになります。

ip_ruby_cmd(void * 0x00000000, Tcl_Interp * 0x02443548, int 4, Tcl_Obj * const * 0x0240e134) line 1816
EvalObjv(Tcl_Interp * 0x02443548, int 4, Tcl_Obj * const * 0x0240e134, char * 0x08fc03f8, int 31, int 0) line 932 + 25 bytes
Tcl_EvalEx(Tcl_Interp * 0x02443548, char * 0x08fc03f8, int 31, int 262144) line 1393 + 30 bytes
Tcl_EvalObjEx(Tcl_Interp * 0x02443548, Tcl_Obj * 0x02501fa8, int 262144) line 2580 + 21 bytes
Tcl_EvalObjCmd(void * 0x00000000, Tcl_Interp * 0x02443548, int 3, Tcl_Obj * const * 0x0244b560) line 624 + 18 bytes
TclExecuteByteCode(Tcl_Interp * 0x02443548, ByteCode * 0x024756a0) line 874 + 25 bytes
Tcl_EvalObjEx(Tcl_Interp * 0x02443548, Tcl_Obj * 0x02434e28, int 0) line 2733 + 13 bytes
TclObjInterpProc(void * 0x0249ebf0, Tcl_Interp * 0x02443548, int 2, Tcl_Obj * const * 0x0244b558) line 1001 + 21 bytes
TclExecuteByteCode(Tcl_Interp * 0x02443548, ByteCode * 0x024757a0) line 874 + 25 bytes
Tcl_EvalObjEx(Tcl_Interp * 0x02443548, Tcl_Obj * 0x025027a0, int 131072) line 2733 + 13 bytes
TkInvokeMenu(Tcl_Interp * 0x02443548, TkMenu * 0x024754a0, int 0) line 1119 + 28 bytes
TkWinHandleMenuEvent(HWND__ * * 0x0240ec34, unsigned int * 0x0240ec38, unsigned int * 0x0240ec3c, long * 0x0240ec40, long * 0x0240ec28) line 989 + 23 bytes
TkWinMenuProc(HWND__ * 0x000f05d8, unsigned int 273, unsigned int 1, long 0) line 861 + 25 bytes
USER32! 77e0a420()
USER32! 77de4605()
USER32! 77de5b77()
Tcl_DoOneEvent(int -3) line 889 + 9 bytes
lib_eventloop_core() line 698 + 10 bytes
lib_eventloop_main() line 804 + 16 bytes
rb_ensure(unsigned long (void)* 0x02dc1040 lib_eventloop_main(void), unsigned long 2, unsigned long (void)* 0x02dc16f0 lib_eventloop_ensure(void), unsigned long 0) line 5233 + 7 bytes
lib_eventloop_launcher() line 840 + 23 bytes
lib_mainloop() line 860 + 9 bytes
rb_call0() line 5565 + 157 bytes
rb_call(unsigned long 46032112, unsigned long 46032568, unsigned long 9897, int 1, const unsigned long * 0x0240f06c, int 0) line 5787 + 40 bytes
rb_eval(unsigned long 45992824, RNode * 0x02a96df8) line 3205 + 212 bytes
rb_call0() line 5694 + 13 bytes
rb_call(unsigned long 45959416, unsigned long 45992824, unsigned long 9897, int 0, const unsigned long * 0x00000000, int 0) line 5787 + 40 bytes
rb_eval(unsigned long 44864088, RNode * 0x02ab6f88) line 3205 + 212 bytes
eval_node() line 1296 + 13 bytes
ruby_exec_internal() line 1476 + 18 bytes
ruby_exec() line 1495
ruby_run() line 1511 + 5 bytes
main() line 40
RUBY! mainCRTStartup + 227 bytes
KERNEL32! 77e7893d()

次に longjmp して setjmp に戻ることになりますが、このままではデバッガの
ステップ実行が setjmp に移ってくれません。そこで、下のようなパッチを
当て、dummy1 にブレークポイントを置くことで setjmp の直後から再開できる
ようにします。

Index: eval.c
===================================================================
RCS file: /src/ruby/eval.c,v
retrieving revision 1.748
diff -u -w -b -p -r1.748 eval.c
--- eval.c	5 Jan 2005 03:49:50 -0000	1.748
+++ eval.c	1 Feb 2005 07:20:04 -0000
@@ -925,7 +925,13 @@ static struct tag *prot_tag;
 #define PROT_YIELD  INT2FIX(3)	/* 7 */
 #define PROT_TOP    INT2FIX(4)	/* 9 */
 
-#define EXEC_TAG()    (FLUSH_REGISTER_WINDOWS, setjmp(prot_tag->buf))
+static void dummy1(void)
+{
+}
+
+static int dummy2;
+
+#define EXEC_TAG()    (FLUSH_REGISTER_WINDOWS, dummy2 = setjmp(prot_tag->buf), dummy1(), dummy2)
 
 #define JUMP_TAG(st) do {		\
     ruby_frame = prot_tag->frame;	\


例外発生後、コールスタックは次のようになります。

dummy1() line 929
rb_ensure(unsigned long (void)* 0x02dc1040 lib_eventloop_main(void), unsigned long 2, unsigned long (void)* 0x02dc16f0 lib_eventloop_ensure(void), unsigned long 0) line 5232 + 27 bytes
lib_eventloop_launcher() line 840 + 23 bytes
lib_mainloop() line 860 + 9 bytes
rb_call0() line 5565 + 157 bytes
rb_call(unsigned long 46032112, unsigned long 46032568, unsigned long 9897, int 1, const unsigned long * 0x0240f06c, int 0) line 5787 + 40 bytes
rb_eval(unsigned long 45992824, RNode * 0x02a96df8) line 3205 + 212 bytes
rb_call0() line 5694 + 13 bytes
rb_call(unsigned long 45959416, unsigned long 45992824, unsigned long 9897, int 0, const unsigned long * 0x00000000, int 0) line 5787 + 40 bytes
rb_eval(unsigned long 44864088, RNode * 0x02ab6f88) line 3205 + 212 bytes
eval_node() line 1296 + 13 bytes
ruby_exec_internal() line 1476 + 18 bytes
ruby_exec() line 1495
ruby_run() line 1511 + 5 bytes
main() line 40
RUBY! mainCRTStartup + 227 bytes
KERNEL32! 77e7893d()

Tclの環境を一気に飛び越してしまっている事がわかります。Tclではスタック変数を
PushCallFrame することが行われているので、不正なスタック領域がフレームチェーン
に残ってしまうことになります。そこは解放されたスタックなので、以後の自動変数の
定義で利用され、値が変更されていくことになります。

ruby_eval 中に ruby_stop してもいいのかわかりませんが、とりあえず下のように
すると落ちなくなりました。本当は Tcl_SetResult などを使って、Tcl の環境を
安全に通過した後、Tcl_GetResult を見て lib_eventloop_launcher で exit(2)
すべきなのかもしれません。(試してないので不可能かもしれませんが)

Index: tcltklib.c
===================================================================
RCS file: /src/ruby/ext/tk/tcltklib.c,v
retrieving revision 1.1
diff -u -w -b -p -r1.1 tcltklib.c
--- tcltklib.c	25 Jan 2005 14:31:44 -0000	1.1
+++ tcltklib.c	1 Feb 2005 06:54:25 -0000
@@ -1813,7 +1813,7 @@ ip_ruby_cmd(clientData, interp, argc, ar
 
             rb_thread_critical = thr_crit_bup;
 
-            rb_raise(rb_eSystemExit, RSTRING(res)->ptr);
+            ruby_stop(0);
 
         } else if (rb_obj_is_kind_of(res, eLocalJumpError)) {
             VALUE reason = rb_ivar_get(res, ID_at_reason);

また、以下の変更は、本来のバグを隠してしまうので revert すべきだと思います。

Mon Jan 31 13:13:35 2005  Hidetoshi NAGAI  <nagai@ai.kyutech.ac.jp>

	* ext/tk/tcltklib.c: add invalid namespace check

	* ext/tk/lib/multi-tk.rb: add invalid_namespace? method

	* ext/tk/lib/remote-tk.rb: ditto


# tcltk8.3.3 と ruby(29 Jan 2005 15:00) を VisaulC++ でデバッグビルドした環境で
# テストしました。

# しかし、C++Builderのデバッガはなぜか「そこにある」デバッグ情報 tds を呼んでくれず、
# てこずりました。C++Builder1 を入れてバイナリにデバッグ情報を直接埋め込む tlink32
# を使ったりしましたが、ruby をビルドできず、結局 VisualC++ のデバッガに助けられました。



In This Thread