メインコンテンツ

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

CERT C: Rec.MEM12-C

リソースの使用時や解放時にエラーが発生して関数を出る場合は、goto 連鎖の使用を検討する

説明

ルール定義

リソースの使用時や解放時にエラーが発生して関数を出る場合は、goto 連鎖の使用を検討します。1

Polyspace 実装

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

  • メモリ リーク

  • リソース リーク

すべて展開する

問題

メモリ リークは、malloccallocrealloc または new を通して割り当てられたメモリのブロックが解放されない場合に発生します。メモリが関数内で割り当てられる場合、以下の条件を満たせば欠陥は生じません。

  • 関数内で、free または delete を使用してメモリが解放される。

  • 関数が malloccallocrealloc または new によって割り当てられたポインターを返す。

  • 関数がグローバル変数またはパラメーターにポインターを格納する。

リスク

malloc などの動的メモリ割り当て関数はヒープ上のメモリを割り当てます。使用後にメモリを解放しないと、別の割り当てに使用できるメモリの量が減少します。メモリに制限がある組み込みシステムでは、プログラムの実行中でも、使用可能なヒープ メモリを使い果たす可能性があります。

修正方法

動的に割り当てられたメモリへのアクセスがあるスコープを確認します。このスコープの最後でメモリ ブロックを解放します。

メモリのブロックを解放するには、メモリの割り当て時に使用したポインターに対して関数 free を使用します。次に例を示します。

ptr = (int*)malloc(sizeof(int));
//...
free(ptr);

同じモジュール内のメモリは同じ抽象化レベルで割り当てと解放を行うことをお勧めします。次の例の場合、func はメモリの割り当てと解放を同じレベルで行っていますが、func2 はそうではありません。

void func() {
  ptr = (int*)malloc(sizeof(int));
  {
    .//..
  }
  free(ptr);
}

void func2() {
  {
   ptr = (int*)malloc(sizeof(int));
   //...
  }
  free(ptr);
}
CERT-C Rule MEM00-C を参照してください。

例 - 関数の終了前に動的メモリが未解放
#include<stdlib.h>
#include<stdio.h>

void assign_memory(void)
{
    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) 
        {
         printf("Memory allocation failed");
         return;
        }


    *pi = 42;
    /* Defect: pi is not freed */
} //Noncompliant

この例では、malloc によって pi が動的に割り当てられています。関数 assign_memory はメモリを解放せず、pi も返しません。

修正 — メモリを解放

1 つの修正方法として、pi が参照するメモリを関数 free を使用して解放することができます。関数 free は関数 assign_memory が終了する前に呼び出さなければなりません。

#include<stdlib.h>
#include<stdio.h>

void assign_memory(void)
{
    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) 
        {
         printf("Memory allocation failed");
         return;
        }
    *pi = 42;

    /* Fix: Free the pointer pi*/
    free(pi);                   
}
修正 — 動的割り当てからポインターを返す

もう 1 つの修正方法は、ポインター pi を返すことです。pi を返すことにより、assign_memory を呼び出している関数が pi を使用してメモリ ブロックを解放できるようになります。

#include<stdlib.h>
#include<stdio.h>

int* assign_memory(void)
{
    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) 
        {
            printf("Memory allocation failed");
            return(pi);
        }
    *pi = 42;

    /* Fix: Return the pointer pi*/
    return(pi);                   
}
問題

"リソース リーク" が発生するのは、FILE ポインターを使用してファイル ストリームを開いているが、それを閉じる前に以下の処理を行った場合です。

  • ポインターのスコープの終了。

  • 別のストリームへのポインターの割り当て。

リスク

ファイル ハンドルを明示的に、できるだけ早期に解放しないと、リソースが使い果たされることによりエラーが発生することがあります。

修正方法

FILE ポインターを、そのスコープの終了前か、そのポインターを別のストリームに割り当てる前に閉じます。

例 - FILE ポインターがスコープの終了前に解放されない
#include <stdio.h>

void func1( void ) {
    FILE *fp1;
    fp1 = fopen ( "data1.txt", "w" );
    fprintf ( fp1, "*" );

    fp1 = fopen ( "data2.txt", "w" ); //Noncompliant
    fprintf ( fp1, "!" );
    fclose ( fp1 );
}

この例では、ファイル ポインター fp1 がファイル data1.txt を指しています。fp1 は、data1.txt のファイル ストリームから明示的に分離される前に、別のファイル data2.txt へのアクセスに使用されています。

修正 — FILE ポインターを解放

1 つの修正方法として、fp1data1.txt のファイル ストリームから明示的に分離します。

#include <stdio.h>

void func1( void ) {
    FILE *fp1;
    fp1 = fopen ( "data1.txt", "w" );
    fprintf ( fp1, "*" );
    fclose(fp1);

    fp1 = fopen ( "data2.txt", "w" );                  
    fprintf ( fp1, "!" );
    fclose ( fp1 );
}

チェック情報

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