From: Takaaki Tateishi Date: 2004-12-14T23:23:41+09:00 Subject: [ruby-dev:25195] Re: double free problem Yukihiro Matsumoto wrote: > externが設定したrealpath(3)の戻り値のtypeは"s"なので、返さ > れた値をfreeする。 > > しかし、realpath(3)は2番目の引数を返す。つまり、戻されたポ > インタは「"\0" * LIBC::PATH_MAX」で作られたStringのptrであ > る。 これについて、意見を伺いたいと思っています。現状の実装では、戻り 値の型をconst char * にするか、あるいは、void * として受けること で解決できます。 そして今回の問題は、Cで文字列を扱う場合にはfreeするかどうかはユー ザが決定すべきものですが、import.rb では、そのfreeの権限を型(プロ トタイプ)から推測させてしまっていることが根本的な原因です。 このため、このような推測をやめるという方法もとれますが、この場合、 プロトタイプ宣言をexternメソッドに与える際に、別の方法でユーザか らfreeするかどうかを明示的に与えてもらう必要があります。どちらの 方が良いでしょうか? (私は現状のままがいいかなと思っています) あるいは、他の方法など提案してもらえると助かります。 少し詳細に書くと以下の通りです。 # 今回の問題に限らず、不明な点などあれば聞いてください。 引数側と、戻り値側で2度同じものがfreeされるという問題なのですが、 (1)引数側をfreeしない、 (2)戻り値側をfreeしない という2つの解決方法があります。 このどちらか、あるいは両方を実現するためには、 (a)プロトタイプ宣言として const char *を指定するなど、ユーザに 任せるという方法と、 (b)dl側で常にfreeしない という2つの実現方法があります。現状では、(a)を採用してchar* や const char* などのプロトタイプ宣言から、どうするべきかを判断し ています。 この判断として、次のことを考えています。文字列の場合、Rubyのオ ブジェクトが保持している文字列をそのまま渡すか、あるいは、渡さ ないかという2つの選択肢があります。プロトタイプ宣言として const がついているということは、その文字列の中身は変更されないという ことを意味するので、この場合にはRubyが保持している文字列 RSTRING(value)->ptr をそのまま渡します。そうでない場合にはコピ ーを作って渡すようにしています。 コピーを作る場合、そのコピーはrb_tainted_str_new2()によってRuby 上のオブジェクトとなるのですが、rb_tainted_str_new2()内では引数 で与えた文字列をさらにコピーするので、結局そのコピーをfreeする 必要があります。このためにknuさんの示したコードでは引数側がfree されています。 一方で、戻り値についても同様のことが言えます。Ruby側へ文字列と して返すためには rb_tainted_str_new2() を使っています。このため 戻ってきた文字列はrb_tainted_str_new2()によってコピーされてRuby の文字列となります。元のポインタは不要になるので free するので すが、knuさんのコードでは、(たぶん)ここで2度目の free が起きて います。 ここからが迷っているところなのですが、引数をfreeしないようにす るためには、const char * を使うのですが、Rubyが保持している文 字列への変更を許してしまうので、なるべくなら避けたいと思ってい ます。そこでもう一方の返り値をfreeしないようにするためには、引 数の型と同じ感覚で const char * を返り値の型として欲しいと思っ ています。 これでよければ解決するのですが、プロトタイプ宣言と若干異なる型 になる点で反対される方がいらっしゃると思います。この場合、別途 freeするしないを判断するためのキーワードを導入することになりま す。 -- Takaaki Tateishi