back numbers

7.10.2008

Cost of Exception on C++

A curious AIX crash
Yaccに見つかった年代物のバグと違い、こちらは現在でも或いは今だからこそ遭遇する類のバグである。C++を実際の開発で運用する場合には非常に多くのルールを設ける必要がある。それは開発者のメンタルモデルや仕様との接続といった上位のセマンティックスから、プログラムを実行する命令プロセッサやアーキテクチャなどの下位のセマンティックスまでに渡り、現実的には数多くの罠が仕掛けられたレースを駆け抜けて行くようである。冒頭のリンクは、下のレイヤで見つかった罠を如何にして潜り抜けたかという話である。

Filodejは複数のプラットフォームに対応したアプリケーションを開発している。ある日発見されたバグはAIXでのみ発現するものだった。それは次の部分でSEGVを起こした。
virtual T const& get( unsigned int id ) const
{ <------------ The crash was right here before anything happen
if ( id < static_cast( m_values.size() ) )
return get_internal( id );
throw ExceptionNotFound( LOCARG, id, count() );
UNREACHABLE_RETURN( T() );
}
- Filodej's Linux primerより
対応する命令列は次の箇所。
0x39927250  7c0802a6    mflr  r0            ;; Function call prologue routine
0x39927254 93e1fffc stw r31,-4(r1)
0x39927258 93c1fff8 stw r30,-8(r1)
0x3992725c 93a1fff4 stw r29,-12(r1)
0x39927260 9381fff0 stw r28,-16(r1)
0x39927264 9361ffec stw r27,-20(r1)
0x39927268 9341ffe8 stw r26,-24(r1)
0x3992726c 90010008 stw r0,0x8(r1)
0x39927270 9421ed30 stwu r1,-4816(r1) ;; Here was the place of the segfault

疑惑は r1 の値か -4816(r1) の値に絞られる。C++で開発をしていると、この手の不具合は"割と"見られる症状である。例えばプログラムが pthread を利用している場合がそうだ。スレッドスタックの伸張時、スタックに割当てられたインスタンスオブジェクトに未初期化フィールドがあるとこの手のバグに遭遇する。しかし今回のケースでは違っていた。

AIXに限らず、現代的なOSではguard pageという不正アクセスの検知領域が存在する。先のプログラムは -4816(r1) がこのguard page内を参照したためにSEGVを投げていたのだ。では何故そこを参照したのか?最初に載せたC++のコードで、例外を投げる部分が問題だったのである。氏が備えをした例外は、発生時にヒープ割当てを起こさないためにスタックに確保したオブジェクトだった。そしてこれはワイドキャラクターの配列として実装されていた。この比較的大きな例外オブジェクトはAIXが用意するスタックサイズを飛び抜けてしまい、またそれがヒープ確保されないがために、明示的にguard pageを叩いたのだ。結果的に見れば、これはソフトウェアをクラックする常套手段でもある。

0 件のコメント:

tags

Profile

Taito, Tokyo, Japan
明けども明けども次の埒
hiro.kosh@gmail.com