メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

CERT C: Rule MEM30-C

Do not access freed memory

説明

ルール定義

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

Polyspace 実装

このチェッカーは、以下をチェックします。

  • 前に解放したポインターへのアクセス

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

すべて展開する

問題

前に解放したポインターへのアクセスは、メモリのブロックを関数 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;
}
問題

前に解放したポインターの解放は、関数 free を使用してポインターを解放した後、そのポインターに割り当てられていたメモリを解放しようとした場合に発生します。

リスク

前に解放したポインターに関連付けられていたメモリを解放しようとすると、プログラムのメモリ管理が破損して、メモリ リークが発生する可能性があります。この欠陥は、攻撃者にメモリへのアクセスと任意のコードの実行を可能にします。

修正方法

この欠陥を回避するには、解放したポインターに NULL を割り当てます。ポインターに関連付けられたメモリにアクセスする前にポインターの NULL 値をチェックします。この方法により、解放されているブロックにアクセスするのを避けることができます。

例 — 前に解放したポインターの解放
#include <stdlib.h>
#include <stdio.h>
int getStatus();
void double_deallocation(void)
{
	int* pi = (int*)malloc(sizeof(int));
	if (pi == 0) return;

	*pi = 2;
	/*...*/
	if(getStatus()==1)
	{
		/*...*/
		free(pi);
	}
	free(pi); //Noncompliant //Noncompliant
}

2 番目の free ステートメントは、pi が参照しているメモリのブロックを解放しようとしますが、ポインター pi はコードの if ブロックで既に解放されている可能性があります。2 番目の free ステートメントは、コード内のメモリ リークとセキュリティ脆弱性を引き起こす可能性があります。Polyspace® は、2 番目の free ステートメントにフラグを設定します。

修正 — free を呼び出す前のポインターのチェック

1 つの修正方法として、解放したポインターに NULL を割り当て、解放前にポインターの NULL をチェックすることができます。

#include <stdlib.h>
#include <stdio.h>
int getStatus();
void double_deallocation(void)
{
	int* pi = (int*)malloc(sizeof(int));
	if (pi == 0) return;

	*pi = 2;
	/*...*/
	if(getStatus()==1)
	{
		/*...*/
		if(pi!=NULL)
		{
			free(pi);
			pi= NULL;
		}
	}
	/*...*/
	if(pi!=NULL)
	{
		free(pi);
		pi= NULL;
	} //Compliant
}

このケースでは、ポインター pi に割り当てられたメモリがまだ解放されていない場合にのみ解放されます。

例 – サイズがゼロの可能性のある、過去に再割り当てされたポインターの解放
#include <stdlib.h>
  
void reshape(char *buf, size_t size) {
  char *reshaped_buf = (char *)realloc(buf, size);
  if (reshaped_buf == NULL) {
    free(buf); //Noncompliant
  }
}

この例では、関数 reshape() の引数 size がゼロになり、その結果、realloc() でサイズがゼロの再割り当てが行われる可能性があります。GNU® ライブラリなどの一部の実装では、サイズがゼロの再割り当てによってメモリが解放されると、二重解放の欠陥につながります。

修正 – サイズがゼロの再割り当てを防止する

1 つの修正方法として、使用する前に realloc() のサイズ引数の値がゼロかどうかをチェックします。サイズ引数がゼロの場合は、メモリを再割り当てするのではなく、単に解放します。

#include <stdlib.h>

void reshape(char *buf, size_t size) {
  if (size != 0) {
    char *reshaped_buf = (char *)realloc(buf, size);
    if (reshaped_buf == NULL) {
      free(buf);
    }
  }
  else {
    free(buf);
  }

}

チェック情報

グループ: Rule 08.メモリ管理 (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.