組み込みソフトウェア・バグの分類学,その傾向と対策 ――ツールを利用してバグ対策の手間を軽減
《バグ・クラス No.1》
メモリ・リーク
●メモリ・リークとは?
用意されたヒープ領域の中の使用できる領域がだんだん減少し,最終的に利用できるメモリ領域がなくなってしまう現象を「メモリ・リーク」と呼びます.例えばmallocを使用して動的なメモリ領域を確保した場合,その後,freeによってその領域を解放することでそのメモリ領域を再利用できるようになります.この解放を忘れると,それがメモリ・リークの原因となります.長時間稼動するプログラムにおいて,メモリ・リークが発生している場合に,システムの性能が低下したり,最悪,停止する恐れがあります.
また,メモリ・リークを,ファイルやネットワーク,OSなどのリソースに拡張した概念として,「リソース・リーク」という考え方があります.例えば,ファイルの読み書きを行う場合,最初にfopenでファイルを開く処理を行い,処理終了時にfcloseでファイルを閉じる処理を行います.このとき,ファイルを閉じ忘れると,リソース・リークが発生します.
●メモリ・リークの例
簡単な例:
/* 動的に確保された領域が解放されていない */
char *p=malloc(100);
return 0;
●メモリ・リークの対策
メモリ・リークがよく発生するのは,例えば次のようなケースです.すなわち,領域を確保した後,エラー処理関数などの複数の関数に処理がまたがった場合,その処理内の分岐条件によってreturnのポイントが複数存在し,領域の解放が漏れてしまうというケースです.
この対策として,動的メモリの管理(確保と解放)は,エラー関数などの下位関数で行わず,同じ上位関数内で行うようにしましょう.
●静的解析ツールによるバグ検出
コーディング・ルール・チェッカでメモリ・リークを検出できるものは少ないと思います.処理が複数の関数(ファイル)にまたがる場合に,その関数間の領域の確保と解放の処理の組み合わせを正確に追えない,という問題があります.
図2にCodeSonarによる解析例を示します.これはオープン・ソースのgnuchessプログラムを解析し,ファイル間にまたがるリークを検出した例です.
図2 メモリ・リークの解析例(Leak Warning)※ 図をクリックすると拡大できます
ここでは,「ファイル・ポインタのリソース・リークの可能性がある」というワーニングを出力しています.関数が304行目でリターンした場合,297行目で,オープンされたファイル・ポインタがリークとなるかもしれません.ここでの問題は,ファイル・ポインタがローカル変数であるrfpに格納されていることです.そのため,関数が304行目でリターンしたときにrfpのスコープ外となり,オープンされたファイル・ポインタは永久に失われてしまいます.
リークが発生する箇所までのパスは,図2のように赤色で表示されています.ユーザは,プラス(+)マークをクリックすることで,299行目のcheck_magic( )呼び出しを精査し,rfpがクリーンナップされていないことを確認できます.