メインコンテンツ

CERT C++: MEM50-CPP

Do not access freed memory

説明

ルール定義

解放されたメモリにアクセスしないようにします。1

Polyspace 実装

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

  • 範囲外にアクセスするポインター

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

  • 前に解放したポインターの使用

すべて展開する

問題

範囲外にアクセスするポインターは、ポインターが範囲外でデリファレンスされる場合に発生ます。

ポインターにアドレスが割り当てられると、そのポインターにメモリのブロックが関連付けられます。そのポインターを使用してそのブロック外のメモリにアクセスすることはできません。

リスク

範囲外のポインターのデリファレンスは未定義の動作です。予測不能な値を読み取ったり、許可されていない位置へのアクセスを試みてセグメンテーション違反が発生したりする可能性があります。

修正方法

修正方法は欠陥の根本原因によって異なります。たとえば、ループ内のポインターをデリファレンスする際、次のいずれかの状況が発生したとします。

  • ループの上限が大きすぎる。

  • ポインターをインクリメントするためにポインター演算を使用する際、不適切な値でポインターを進めている。

この問題を修正するには、ループの範囲またはポインターのインクリメント値を変更する必要があります。

多くの場合、結果の詳細には欠陥につながる一連のイベントが表示されます。そのシーケンス内のどのイベントについても修正を実装できます。結果の詳細にイベント履歴が表示されない場合は、ソース コード内で右クリック オプションを使用して逆のトレースを行い、これまでの関連するイベントを確認できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈も参照してください。

以下の修正例を参照してください。

問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。

例 - 範囲外にアクセスするポインター エラー
int* Initialize(void)
{
 int arr[10];
 int *ptr=arr;

 for (int i=0; i<=9;i++)
   {
    ptr++;
    *ptr=i; //Noncompliant
    /* Defect: ptr out of bounds for i=9 */
   }

 return(arr);
}

ptr には、サイズ 10*sizeof(int) のメモリ ブロックを指すアドレス arr が割り当てられます。for ループで、ptr は 10 回インクリメントされます。ループの最後の反復で、ptr は割り当てられたメモリ ブロックの外を指します。したがって、デリファレンスはできません。

修正 — ポインターが範囲内にあることをチェック

1 つの修正方法として、ptr のインクリメントとデリファレンスの順序を逆にすることができます。

int* Initialize(void)
{
 int arr[10];
 int *ptr=arr;

 for (int i=0; i<=9;i++)
     {
      /* Fix: Dereference pointer before increment */
      *ptr=i;
      ptr++;
     }

 return(arr);
}

最後のインクリメントの後で ptr は割り当てられたメモリ ブロックの外を指しますが、もうデリファレンスされることはありません。

問題

以前に割り当て解除したポインターの解放は、メモリのブロックが関数 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 */
 }
問題

前に解放したポインターの使用は、メモリのブロックを関数 free を使用して解放した後、そのメモリ ブロックにアクセスした場合に発生します。

リスク

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

修正方法

修正方法は欠陥の根本原因によって異なります。メモリを後で解放することを意図しているのか、またはアクセスする前に別のメモリ ブロックをポインターに割り当てることを意図しているのかを確認します。

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

例 - 前に解放したポインターの使用エラー
#include <stdlib.h>
#include <stdio.h>
 int increment_content_of_address(int base_val, int shift)
   { 
    int j;
    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) return 0;

    *pi = base_val;
    free(pi);

    j = *pi + shift; //Noncompliant
    /* Defect: Reading a freed pointer */
 
    return j;
   }

free ステートメントにより、pi が参照するメモリのブロックが解放されます。そのため、free ステートメント後の pi のデリファレンスは有効ではありません。

修正 — 使用後にポインターを解放

1 つの修正方法として、最後のアクセス インスタンスの後にのみポインター pi を解放することができます。

#include <stdlib.h>

int increment_content_of_address(int base_val, int shift)
{
    int j;
    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) return 0;

    *pi = base_val;

    j = *pi + shift;
    *pi = 0;

    /* Fix: The pointer is freed after its last use */
    free(pi);               
    return j;
}

チェック情報

グループ: 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.