[ruby-dev:48598] Re: StringValuePtrでnull終止されてない文字列の作り方

From: "NARUSE, Yui" <naruse@...>
Date: 2014-10-06 14:02:56 UTC
List: ruby-dev #48598
まず、CRubyとしてはStringValuePtrがNUL終端する保証はしていませんから、仮にnull-terminateしてないことにより問題が起きるならば、
それはruby-pgのバグです。
とはいえ、Railsアプリがないと再現出来ないようではテストするにもつらいでしょうねぇ。

さて、nul-terminateされていないStringを作る方法ですが、Rubyレベルではありません(ないようにしているはずです)。
拡張ライブラリでは作ることが一応でき、例えば以下のような感じでしょうか。

diff --git a/ext/-test-/string/nulterm.c b/ext/-test-/string/nulterm.c
new file mode 100644
index 0000000..78f5af3
--- /dev/null
+++ b/ext/-test-/string/nulterm.c
@@ -0,0 +1,44 @@
+#include "ruby/ruby.h"
+
+#define STR_NOEMBED      FL_USER1
+#define STR_EMBED_P(str) (!FL_TEST((str), STR_NOEMBED))
+
+#define STR_SET_EMBED_LEN(str, n) do { \
+    long tmp_n = (n);\
+    RBASIC(str)->flags &= ~RSTRING_EMBED_LEN_MASK;\
+    RBASIC(str)->flags |= (tmp_n) << RSTRING_EMBED_LEN_SHIFT;\
+} while (0)
+
+#define STR_SET_LEN(str, n) do { \
+    if (STR_EMBED_P(str)) {\
+ STR_SET_EMBED_LEN((str), (n));\
+    }\
+    else {\
+ RSTRING(str)->as.heap.len = (n);\
+    }\
+} while (0)
+
+/* return coderange without scan */
+static VALUE
+str_chop_without_terminate(VALUE str)
+{
+    STR_SET_LEN(str, RSTRING_LEN(str)-1);
+    return Qnil;
+}
+
+/* scan coderange and return the result */
+static VALUE
+str_null_terminated_p(VALUE str)
+{
+    if ((size_t)RSTRING_LEN(str) == rb_str_capacity(str)) return Qfalse;
+    if (*RSTRING_END(str) != 0) return Qfalse;
+
+    return Qtrue;
+}
+
+void
+Init_nulterm(VALUE klass)
+{
+    rb_define_method(klass, "chop_without_terminate",
str_chop_without_terminate, 0);
+    rb_define_method(klass, "null_terminated?", str_null_terminated_p, 0);
+}
diff --git a/test/-ext-/string/test_nulterm.rb
b/test/-ext-/string/test_nulterm.rb
new file mode 100644
index 0000000..de5f2ba
--- /dev/null
+++ b/test/-ext-/string/test_nulterm.rb
@@ -0,0 +1,13 @@
+# coding: ascii-8bit
+require 'test/unit'
+require "-test-/string/string"
+
+class Test_StringNulterm < Test::Unit::TestCase
+  def test_usascii
+    str = Bug::String.new("foo")
+    assert_equal true, str.null_terminated?
+    str.chop_without_terminate
+    assert_equal "fo", str
+    assert_equal false, str.null_terminated?
+  end
+end

2014年10月5日 22:22 madoka yamamoto <yamamotomadoka@gmail.com>:
> 山本ともうします。
>
> trunkのRubyでPostgreSQL拡張ライブラリを使っていて、null終止されない文字列がPostgreSQL拡張ライブラリに渡っていることに起因するエラーに遭遇しました。
> 参考: https://github.com/ged/ruby-pg/pull/5
>
> PostgresSQL拡張ライブラリのソースを見ていると、Rubyから文字列を取り出すのにStringValuePtrが使われており、これが原因だと思われるのですが、どのような経緯を経たことが原因でnull終止されない文字列が生成されるかが解らず、このエラーの再現に苦労しています。
>
> このような、StringValueCStrとStringValuePtrの差異が出るような文字列を作る方法がRubyレベル、もしくは拡張ライブラリレベル
> でありますでしょうか?
>



-- 
NARUSE, Yui  <naruse@airemix.jp>

In This Thread