2013-05-20 4 views
40

私はhttp://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.phpのガイドに基づいてCZMQ用のOCamlバインディングを作成しましたが、これはかなりうまくいくようです。例えば、ここにzstr_sendです:シグナルハンドラからOCamlでラップされたZeroMQコードを呼び出す

CAMLprim value 
caml_zstr_send(value socket_val, value string_val) 
{ 
    CAMLparam2 (socket_val, string_val); 

    void *sock = CAML_CZMQ_zsocket_val(socket_val); 
    char *string = String_val(string_val); 
    int rc = zstr_send(sock, string); 

    CAMLreturn (Val_int(rc)); 
} 

は、私は自分のコードのほとんどにこれらのバインディングを使用してうまくメッセージを送受信することができます。しかし、私は、シグナルハンドラの内部で送受信を行い、他のコードのバックグラウンドでメッセージを受け渡しするというシナリオを持っています。この簡単な例ください:トップレベルから

open ZMQ 
exception SocketBindFailure 

let bg_ctx = zctx_new();; 
let pub_sock = zsocket_new bg_ctx ZMQ_PUB;; 

let handler _ = 
    print_endline "enter handler"; 
    print_endline (string_of_int (zstr_send pub_sock "hello")); 
    print_endline "end handler"; 
;; 

let() = 
    (try (
     (* bind pub socket *) 
     let rc = zsocket_bind pub_sock "tcp://*:5556" in 
     if (rc < 0) then (raise SocketBindFailure); 

     Sys.set_signal 
      Sys.sigalrm 
      (Sys.Signal_handle handler); 

     ignore 
      (Unix.setitimer 
       Unix.ITIMER_REAL 
       { Unix.it_interval = 0.01 ; Unix.it_value = 0.01 }); 

     (* do some work *) 
    ) 
    with 
    | SocketBindFailure -> raise SocketBindFailure) 
;; 

を、これは出力で失敗します。OCamlのと似

enter handler 
0 
end handler 
Fatal error: exception Sys_blocked_io 

Cコードは、上記だけで正常に動作します。 OCamlはこの例外の原因となっている式に何を追加していますか?

+11

一般に、シグナルハンドラ内からルーチンを呼び出すのは、コードを監査しているときは赤いフラグです。理由はさまざまですが、その要点はルーチンが非同期で安全であることを100%確実にしなければならないということです。 [詳細はこちら](https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers) –

+1

Iあなたが例外を持った理由を教えてもらえませんが、ハンドラ内でI/Oを試みることで、CやOCamlのように危険な状態になっていると言うことはできません。上記のリンクは、シグナルハンドラの呼び出しを安全に記録するためのレシピを提供します。 –

+0

情報ありがとう、Dave。私は別のアプローチをとっていきます。 – user1494672

答えて

1

二つの潜在的な問題があります。

がシグナルハンドラ内部では、あなただけの非同期シグナル安全関数を呼び出すことができます。ほとんどの機能は非同期信号安全ではありません。

制限の理由は、同じ関数の実行の途中で関数を呼び出すことができるためです。したがって、内部状態が破損する可能性があります。非常に少数の機能が非同期シグナル安全であり、メモリを動的に割り当てるものはありません。 OCamlでは、多くの割り当てが「舞台裏で」行われるため、コードが非同期シグナルセーフではない可能性があります。

あなたの場合、標準出力に書き込む関数を呼び出しています。 Cでは、これはではありません非同期シグナル安全ですが、例外は1つありますが、プリミティブwrite()が例外です。これは生のシステムコール(ファイルディスクリプタで動作します)であり、カーネル自体がシグナルハンドラに気を配らず、制御を戻す前に完全にクリーンアップされるという簡単な理由から非同期シグナル安全です。

信号が非同期たシグナルハンドラ、(ここではケース)から危険な関数を呼び出すと、それ自体が危険な機能を中断C.に未定義の動作ですこれは何かが起こる可能性を意味します - あなたのプログラムの作業を含むをセグメンテーションフォールトやその他のエラーを含むだけでなく、攻撃者が任意のコードを実行できるようにすることができます。これは通常Cのような低レベルの言語に関連しており、通常はOCamlでは発生しません。

OCamlは、OCamlにハンドラが設定されている信号が受信されたときに、セーフポイントまでハンドラの実行を延期します。その結果、ハンドラではボックスなしのの数をref変数に設定するのが安全です。しかし、printのような他の関数は、内部状態を持つ可能性があるため、再入可能ではない可能性があります。一般に、シグナルハンドラ内では、フラグを設定して直ちに返す以上のことを避けてください。 OCamlでは、フラグは31または63ビットの整数でなければならず、ブール値はボックス化されていないためです。 Cでは、フラグはvolatile sig_atomic_tであるか、またはC11のアトミックタイプ(これについてはわかりません)のいずれかでなければなりません。

@TheCodeArtistは、エラーの考えられる他の理由を示します。

関連する問題