SOLID概要

組込み初! 実行時アドレスバグ自動検出機能

~不具合は現行犯で逮捕せよ!~

【第三回】アドレスバグ自動検出の有効な使い方

 

(第一回へ)(第二回へ)

第一回、第二回の解説では、実行時アドレスバグ検出機能がどのようなバグをみつけられるのか、どのような仕組みでバグを検出しているのかについて、ご紹介してきました。第三回となる本稿では、アドレスバグ自動検出機能を利用する際の留意事項や、有効な使い方、活用例などを中心にご紹介していきます。

アドレスバグ自動検出機能は万能ではない

検出しないケースもある

SOLID では、実行時アドレスバグ自動検出の仕組み上、次のようなケースでは意図しないメモリアクセスがあっても、それをバグとして検出しません。

スタック領域の目的外アクセス

スタックオーバーフローの自動検出は、予め設定されたスタックメモリを突き抜けて、メモリが存在しない領域にスタックを積もうとして例外となりバグを検出する仕組みですが、スタック操作以外の動作によりプログラムがスタック領域をアクセスした場合、そこが物理メモリが割り付けられている論理アドレスであれば、この動作をバグとして検出しません。スタック領域のアクセスが不当なのか正当なのかはプログラムの仕様そのものに依存する問題であり、意図的に操作する事もあるので、メモリアクセス命令はそのまま実行されます(図3-1)。

図3-1. スタック領域の目的外アクセス

不連続な配列オーバーラン

配列アクセスにおいて配列の範囲をはみ出した場合には、アドレスサニタイザにより無効アドレスであると判定されてバグ検出ができます(検出の仕組みは第二回を参照してください)。

しかし、図3-2 のように配列の要素を不連続に(ガード領域を飛び越えて)アクセスした場合、たまたまそのアドレスに他の変数が割り当てられていれば、配列としては正しくありませんが、アクセス対象が有効アドレスなのでアドレスサニタイザ機能が有効であってもこれをバグとして検出することが出来ません。

図3-2. 不連続な配列オーバーラン

もっとも、このようなプログラムを書いてしまった場合は、SOLID の静的解析機能が論理的なバグとして指摘する可能性が高いので、実行前に不具合が判明することがほとんどでしょう。

仕様上ライト不可な変数はconst宣言しておくこと

仕様上ライト対象ではない変数に対して、意図しないプログラムが誤ってライト動作をした場合でも、アドレスサニタイザはこれをバグとして検出することはありません。なぜなら、アドレスサニタイザでは対象アドレスの有効・無効のみを判断しておりリード・ライトといったアクセス種別の判定はしていないからです。

変数を確実にライト不可としたい場合は、const宣言により定数に指定してください。const宣言した変数はセクションのメモリ属性がライト不可な .rodata に領域に割り当てられるため、その変数へのライト時にアクセス例外が発生し、確実にバグとして検出可能です。

オーバーヘッドにも注意

アドレスサニタイザモードでデバッグをする場合、以下の理由により図3-3に示すようなメモリ消費量のオーバーヘッドが発生します。

  • アドレス判定のためのチェックルーチンを呼ぶためのコード(図3-3 ①)
  • チェックルーチン(図3-3 ②)
  • 変数の前後のガード領域(図3-3 ③)
  • アドレス有効無効テーブル(図3-3 ④)

図3-3. アドレスサニタイザモード使用時のメモリ消費量オーバーヘッド

プログラムの内容によりオーバーヘッドは大きく変わりますが、アドレス有効無効テーブルは1バイトあたり1ビット必要なので、変数領域の8分の1がオーバーヘッドになります。また内部変数に関しては、ガード領域がスタック使用量増加になるので、スタックオーバーフローが起きないよう、スタックサイズを2倍程度確保する必要があります。その他、変数間のガード領域やチェックルーチンを呼ぶコードのオーバーヘッドもあるため、全体として、アドレスサニタイザを使わない時に比べて1~3割程度多くのメモリを消費します。

また、変数アクセスの都度チェックルーチンが実行されるため、一般的なプログラムにおいてアドレスサニタイザモードで実行した場合は、通常時に比べて実行時間が平均すると2倍程度遅くなります。

なお、MMUを利用したメモリ属性の違反検出(スタック突き抜けや、const宣言変数へのライト動作)においては、メモリ容量や実行時間に対するオーバーヘッドは発生しません。