日報 2016-07-31

exported form https://nippo.wikihub.io/@_ksss_/20160731140352

mrubyでのforkのバグを直した。

https://github.com/iij/mruby-process/pull/7

引数に初期化されてない変数がブロック引数に渡されているので、 これをmruby側で参照すると死ぬ。

そもそもforkにブロック引数はいらない。

mrubyのC側でブロック引数を無くす方法は2つ

  • mrb_yield(mrb, block, mrb_nil_value())
  • mrb_yield_argv(mrb, block, 0, NULL)

mrb_yieldmrb_yield_argvはほぼ同じ関数だが、ブロック引数の取り方に違いがある。 mrb_yieldはブロック引数を1つだけ指定できる。 mrb_yield_argvは0〜いくらでも指定できる。 関数の中身もほぼ同じ

MRB_API mrb_value
mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv)
{
  struct RProc *p = mrb_proc_ptr(b);

  return mrb_yield_with_class(mrb, b, argc, argv, p->env->stack[0], p->target_class);
}

MRB_API mrb_value
mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg)
{
  struct RProc *p = mrb_proc_ptr(b);

  return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class);
}

https://github.com/mruby/mruby/blob/eb7422af581edff0dd4b1f8259598677b4d32793/src/vm.c#L657-L671

要約すると、mrb_yieldではどんなにがんばっても引数を一つ与えていることになるので、意味的にはおかしい。 ブロック引数を0個にしたければ、mrb_yield_argv(mrb, block, 0, NULL)を使うほうがヨサゲ。

どうやってこのバグを発見したかというと、単にコンパイルしたらclangがwarningで教えてくれただけだ。

つまりコンパイルさえすれば発見できる状態にあるが、2年近く放置されていたことになる。(ブロック引数なんかつけなきゃ問題ないので)

そしてこんなミミッチイ事を気にするのは、世界で僕ぐらいだったようである。

おしまい。