ダングリングポインタとは ─ 危険性と回避


ダングリングポインタ

不正なメモリ領域を指すポインタはダングリングポインタ(dangling pointer)と呼ばれる。例えば、解放済みのオブジェクトを参照するポインタ変数などは、無効なメモリアドレスを指していることからダングリングポインタの状態にあると言える。

ダングリングポインタは別名ワイルドポインタ(wild pointer)とも呼ばれる。

目次

スポンサーリンク

ダングリングポインタの例

解放済みのオブジェクトポインタはダングリングポインタの良い例である。

char *p = malloc(1); // メモリ確保
free(p);             // メモリ解放

p;        // ポインタ変数`p`は既にダングリングポインタ
*p = 'c'; // ダングリングポインタへのアクセス(危険)

また、寿命を迎えたオブジェクトを参照し続けているポインタも、ダングリングポインタとなりえる。

char *p = NULL;
{
  // 自動変数`c`の生存期間はスコープ内のみ
  char c = 'c';
  p = &c; // ローカル変数の参照を保持
}
          // ローカル変数`c`の寿命は既に尽きている
p;        // よってポインタpはダングリングポインタ
printf("%c", *p); // 不定な動作を引き起こすため危険

ダングリングポインタの危険性

二重開放

ダングリングポインタの存在によって、開放済みのオブジェクトを二重に開放してしまう危険性を生み出す。メモリの二重開放はプログラムのエラーやクラッシュの原因となるため、細心の注意を払う必要がある。

void *ptr = malloc(1);
free(ptr); // 正常
free(ptr); // エラー発生
// malloc: *** error for object 0x100700000: pointer being freed was not allocated
// *** set a breakpoint in malloc_error_break to debug

不正領域へのアクセス

存在しないメモリ領域へのアクセスによって、本来プログラムが意図しないような動作を引き起こす危険性がある。

char *a = malloc(1);
free(a);  // 開放

char *b = malloc(1);
*b = 'b';
*a = 'a'; // ダングリングポインタへの操作
printf("%c", *b); // "a" ("b"ではない!?)

このように全く関係のないメモリ領域を書き換えてしまう危険性がある。これはソフトウェアの脆弱性やセキュリティーの問題にも繋がるため、大変重要な問題と言える。

加えて、このような不正領域へのデリファレンスは、構造体ポインタへのメンバアクセス時においても、プログラムのクラッシュや強制終了を引き起こさずにそのまま動いてしまうことが多く、問題の発見も遅れやすいという厄介さがある。

ダングリングポインタの回避

ダングリングポインタによる危険性や問題を回避するするためには、ポインタの初期化作業の徹底や、ソフトウェア設計による回避が必要になる。

ポインタ変数の初期化

使われなくなったポインタ変数を明示的にヌルポインタ(NULL)で初期化することで、ダングリングポインタによる影響を回避することが出来る。

char *a = malloc(1);
free(a);

a = NULL; // ダングリングポインタへの参照を断ち切る

free(a);  // ヌルポインタに対する開放処理は無効化される

*a = 'a'; // エラー発生(問題の早期発見に繋がる)

C言語の場合はヌルポインタに対する開放処理は無効化されるため、ポインタの二重開放の問題にも対処することが出来る。

またヌルポインタへの操作はプログラムの強制終了やクラッシュを引き起こすため、脆弱性の早期発見に繋がるほか、不正領域へのアクセスに伴うセキュリティーホールの問題を抑えることに繋がる。

ソフトウェア設計による回避

また、ダングリングポインタの問題を回避するためには、複数のポインタが同一のオブジェクトを参照しないような設計を意識する必要がある。

他にも生のポインタを直接やり取りするのではなく、カプセル化の技法やアクセサ、デストラクタの仕組みを用いてオブジェクトのやり取りや操作をより抽象化したり制限することが求められる。

スマートポインタやARC(特殊なメモリ管理機構)における弱参照と強参照の概念をソフトウェア設計に取り入れることも有効的である。例えばARC(参照カウンタ方式)で弱参照指定されたポインタは、ダングリングポインタになる前に自動的にヌルポインタによる初期化が行われるため、ダングリングポインタの問題が起こらないようになっている。

ARCの実現は難しいが、スマートポインタなら一部の言語で比較的容易に実装ができる。中にはスマートポインタを標準でサポートしている言語も存在する(C++等)。

ただしスマートポインタを用いた設計には自前によるガード処理やアンラップ処理が必要になるため、利用の手間が掛かるという欠点がある。代替案としては、ARCやガベージコレクタを取り入れた言語への移行が考えられる。

プログラミング言語による回避

ガーベッジコレクションを採用したJava、Go等の言語を用いることで、ダングリングポインタとは無縁のプログラミング環境を実現することが出来る。またARCを採用した言語(Swift, Objective-C)や次世代のメモリ管理方式(Ownershipモデル等)を採用したRust等の言語に移行する手もある。

ポインタを直接的に扱わない次世代の言語(Rust)やスクリプト言語(Ruby, Python, JavaScript)の利用も適切である。

広告