メインコンテンツ

CERT C++: MEM51-CPP

Properly deallocate dynamically allocated resources

説明

ルール定義

動的に割り当てられたリソースの割り当てを適切に解除します。1

Polyspace 実装

ルール チェッカーは以下の問題をチェックします。

  • ポインターの無効な削除

  • ポインターの無効な解放

  • 以前に割り当て解除したポインターの解放

すべて展開する

問題

ポインターの無効な削除は次の場合に発生します。

  • delete 演算子を使用してメモリのブロックを解放したが、そのメモリが以前に new 演算子を用いて割り当てられていなかった。

  • 単一オブジェクト表記で delete 演算子を使用してメモリのブロックを解放したが、そのメモリが以前に new 演算子を用いて配列として割り当てられていた。

この欠陥は C++ ソース ファイルに対してのみ適用されます。

リスク

リスクは問題の原因によって異なります。

  • delete 演算子はヒープに割り当てられたメモリのブロックを解放します。以前に割り当てていないヒープ上の位置にアクセスしようとすると、セグメンテーション違反が発生する可能性があります。

  • new の配列表記で以前に割り当てたポインターに対して、delete 演算子の単一オブジェクト表記を使用すると、動作が未定義になります。

この問題により、他のコーディング エラーが明らかになる場合があります。たとえば、delete 演算子または前の new 演算子を別のポインターに対して使用する必要があった可能性があります。

修正方法

修正方法は問題の原因によって異なります。

  • ほとんどの場合、delete ステートメントを削除することによって問題を修正できます。new 演算子を使用してポインターにヒープのメモリが割り当てられていない場合、delete を使用してこのポインターを解放する必要はありません。単に必要に応じてポインターを再利用するか、スコープの終了時にオブジェクトが破棄されるままにしておくことができます。

  • newdelete の表記が一致していない場合、その不一致を修正します。たとえば、単一のオブジェクトを割り当てて、その割り当てを解除する場合は、次の表記を使用します。

    classType* ptr = new classType;
    delete ptr;

    配列オブジェクトを割り当てて、その割り当てを解除する場合は、次の表記を使用します。

    classType* p2 = new classType[10];
    delete[] p2;

この問題によって、間違ったポインターに対して delete または new を使用するなどのコーディング エラーが明らかになった場合は、そのエラーを修正します。

例 - 静的メモリの削除
void assign_ones(void)
{
    int ptr[10];

    for(int i=0;i<10;i++)
        *(ptr+i)=1;  

    delete[] ptr;    //Noncompliant
}

ポインター ptr は、delete 演算子を使用して解放されています。しかし、ptr は動的に割り当てられていないメモリの場所を指しています。

修正: ポインターの割り当て解除を削除

配列 ptr の要素数がコンパイル時にわかっている場合、1 つの修正方法として、ポインター ptr の割り当て解除を削除することができます。

void assign_ones(void) 
{
    int ptr[10];

    for(int i=0;i<10;i++)
        *(ptr+i)=1;  
}
修正 — ポインターの割り当てを追加

配列の要素数がコンパイル時にわかっていない場合、1 つの修正方法として、new 演算子を使用して配列 ptr に動的にメモリを割り当てることができます。

void assign_ones(int num) 
{
    int *ptr = new int[num]; 

    for(int i=0; i < num; i++)
        *(ptr+i) = 1;

    delete[] ptr;
   }
例 - newdelete の不一致
int main (void)
{
    int *p_scale = new int[5];

    //more code using scal

    delete p_scale; //Noncompliant
}

この例では、new int[5] を使用して、p_scale がサイズ 5 の配列に初期化されます。しかし、p_scale は、delete[] ではなく delete によって削除されています。newdelete のペアが対応していません。配列を削除する場合は、大かっこを伴わない delete は使用しないでください。

修正 — deletenew に対応させる

1 つの修正方法として、大かっこを追加して、delete が宣言 new [] と整合性をもつようにします。

int main (void)
{
    int *p_scale = new int[5];

    //more code using p_scale

    delete[] p_scale;
}
修正 — newdelete に対応させる

別の修正方法として、p_scale の宣言を変更します。p_scale を、サイズ 5 の配列ではなく 5 という数値として初期化する場合、異なる構文を使わなければなりません。これを修正するには、初期化での大かっこを小かっこに変更します。delete ステートメントはそのままにしておきます。

int main (void)
{
    int *p_scale = new int(5);

    //more code using p_scale

    delete p_scale;
}
問題

ポインターの無効な解放は、関数 free を使用して解放されたメモリのブロックが、以前に malloccalloc または realloc を使用して割り当てられていなかった場合に発生します。

リスク

関数 free はヒープに割り当てられたメモリのブロックを解放します。以前に割り当てていないヒープ上の位置にアクセスしようとすると、セグメンテーション違反が発生する可能性があります。

この問題により、コーディング エラーが強調表示される場合があります。たとえば、関数 free または前の関数 malloc を別のポインターに対して使用する必要があった可能性があります。

修正方法

ほとんどの場合、free ステートメントを削除することによって問題を修正できます。malloc または calloc を使用してポインターにヒープのメモリが割り当てられていない場合、このポインターを解放する必要はありません。単に必要に応じてポインターを再利用できます。

この問題によって、間違ったポインターに対して free または malloc を使用するなどのコーディング エラーが明らかになった場合は、そのエラーを修正します。

new 演算子で割り当てられたメモリを解放するために関数 free を使用したことが原因で問題が発生している場合は、関数 freedelete 演算子に置き換えます。

例 - ポインターの無効な解放エラー
#include <stdlib.h>

void Assign_Ones(void) 
{
  int p[10];
  for(int i=0;i<10;i++)
     *(p+i)=1; 
 
  free(p);    //Noncompliant
  /* Defect: p does not point to dynamically allocated memory */
}

ポインター p は、関数 free を使用して割り当て解除されています。しかし、p は動的に割り当てられていないメモリの場所を指しています。

修正 — ポインターの割り当て解除を削除

配列 p の要素数がコンパイル時にわかっている場合、1 つの修正方法として、ポインター p の割り当て解除を削除することができます。

#include <stdlib.h>

void Assign_Ones(void)
 {
  int p[10];
  for(int i=0;i<10;i++)
     *(p+i)=1;   
  /* Fix: Remove deallocation of p */
 }
修正 — ポインターを割り当てる

配列 p の要素数がコンパイル時にわかっていない場合、1 つの修正方法として、配列 p に動的にメモリを割り当てることができます。

#include <stdlib.h>

void Assign_Ones(int num) 
{
  int *p;
  /* Fix: Allocate memory dynamically to p */
  p=(int*) calloc(10,sizeof(int)); 
  for(int i=0;i<10;i++)
     *(p+i)=1; 
  free(p); 
}
問題

以前に割り当て解除したポインターの解放は、メモリのブロックが関数 free を使用して複数回解放され、その間に割り当てがない場合に発生します。

リスク

malloccalloc、または realloc を使用してポインターに動的メモリが割り当てられている場合、ポインターはヒープ上のメモリ位置を指します。このポインターに対して関数 free を使用すると、メモリの関連ブロックが再割り当て用に解放されます。メモリのこのブロックを解放しようとすると、セグメンテーション違反が発生する可能性があります。

修正方法

修正方法は欠陥の根本原因によって異なります。最初の割り当て解除と 2 番目の割り当て解除の間でメモリ ブロックをポインターに割り当てることを意図しているかどうかを確認します。そうでない場合は、2 番目の free ステータスを削除します。

メモリ ブロックを解放した後、対応するポインターに NULL を割り当てることをお勧めします。ポインターを解放する前に、NULL 値かどうかをチェックしてエラーを処理します。この方法により、既に解放されているブロックを解放するのを避けることができます。

例 - 以前に割り当て解除したポインターの解放エラー
#include <stdlib.h>

void allocate_and_free(void)
{

    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) return;

    *pi = 2;
    free(pi);
    free (pi);        //Noncompliant
    /* Defect: pi has already been freed */
}

最初の free ステートメントにより、pi が参照するメモリのブロックが解放されます。pi に対する 2 番目の free ステートメントにより、既に解放されたメモリが解放されます。

修正 — 重複する割り当て解除を削除

1 つの修正方法として、2 番目の free ステートメントを削除することができます。

#include <stdlib.h>

void allocate_and_free(void)
{

    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) return;

    *pi = 2;
    free(pi);
    /* Fix: remove second deallocation */
 }

チェック情報

グループ: 06.メモリ管理 (MEM)

バージョン履歴

R2019a で導入


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.