メインコンテンツ

CERT C: Rec.MEM00-C

Allocate and free memory in the same module, at the same level of abstraction

説明

ルール定義

同じモジュール内のメモリは同じ抽象化レベルで割り当てと解放を行います。1

Polyspace 実装

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

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

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

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

すべて展開する

問題

ポインターの無効な解放は、関数 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 */
 }
問題

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

チェック情報

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