2013-06-05 6 views
12

私は再帰を検出したい、つまりその関数が直接的または間接的に呼び出されたかどうかを調べる特定の関数(シグナルハンドラ)を持っています。面倒なことに、関数はある時点では制御できないコードを呼び出し、そのコードは何かを行うことができます。非局所ジャンプがあっても堅牢な再帰を検出する

通常、私はちょうど

void foo() { 
    static int recursed = 0; 
    if(recursed) { 
     ... 
    } 
    recursed = 1; 
    othercode(); 
    recursed = 0; 
} 

ような何かを書くと思いますが、この場合には、私はothercodeは1.に残っrecursedその結果、longjmpまたは抜け出すに類似を使用することができることを心配します私の関数がこのように飛び出された場合、後で呼び出されると再帰的に見えないようにしたい(実際にはlongjmp 'は問題ありません)。

longjmpと思われます。 othercodeは、他のいくつかのin-the-wildコードからの連鎖シグナルハンドラであり、例えば次のようなハンドラが存在します。 SIGSEGVは、文脈を復元するためにlongjmpを使用する(例えば、「障害保護」例外ハンドラとして)。同期シグナルハンドラでlongjmpを使用することは一般的に安全です。いずれにしても、他のコードが安全であるかどうかは特に気にしません。なぜなら、私の制御下にあるものではないからです。

+0

あなたは 'longjmp'の可能性が高いと心配していますか、それとも可能性がありますか? – Patashu

+0

@パタシ:そうです。私の編集を参照してください。 – nneonneo

+0

質問タイトルは、「longjmpを使用しても再入室を検出する」のような、少し具体的に編集する必要があります。 – Patashu

答えて

3

コードが正確にどのように見えるかわかりませんが、静的intの代わりにstatic void *を使用できます。これを1に設定する代わりに、現在のスタックフレームを指すように設定します。ゼロ以外の場合に加えて、recursedの後の次のスタックフレームからの戻りアドレスが実際にfooのコード内の場所を指していて、さらにrecursedがポップされていないことを確認します。

非常に壊れやすく、アーキテクチャに依存します。

+0

また、fooの呼び出し指示の直後にfooのスタックフレームポイントからの戻りアドレスを見る方が簡単かもしれませんが、それは絶対不可能です*。 – morningstar

+0

私はこれを考えました。 1つの小さなコーナーケースを除いて、本当にうまく動作します: 'othercode'が' foo'の上の関数に飛び出し、巨大なスタックバッファを割り当てます(しかし、完全には使用しない) 'foo'を引き起こします。もし私が不運であれば、巨大なスタックバッファはスタックポインタを 'foo'の下に移動し、元の' foo'スタックを保持するので、再帰と非常によく似ています。 – nneonneo

+0

もちろん、これはむしろ起こりそうもないので、おそらくこのソリューションを使用することになるでしょう。しかし、私は理想的には、この問題を持たない解決策を見つけることを望んでいます。 – nneonneo

0

​​のPOSIX規格によると、longjmp()は、シグナルハンドラの内部から安全に呼び出すことのできる呼び出しの1つではありません。 longjmp(3)のドキュメントでは、呼び出し元のコードがsigsetjmp()とsiglongjmp()を使用していることを確認する必要があります。

呼び出し元のコードがシグナルハンドラから飛び出している場合は、 recursed変数をいつ更新するかを、どのように知ることができるかは、この未知のコードがアプリケーションに呼び戻すコールバック関数も制御しない限り、わかりません。

+0

これらは**非同期信号**にのみ適用されます。私は 'SIGSEGV'やその他の通常同期信号用のシグナルハンドラを書いていますので、これは問題ではありません。 – nneonneo

関連する問題