メインコンテンツ

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

CERT C++: MEM35-C

Allocate sufficient memory for an object

説明

ルール定義

オブジェクトに十分なメモリを割り当てます。1

Polyspace 実装

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

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

  • 汚染されたサイズでのメモリの割り当て

  • メモリの割り当て不足

チェッカーの拡張

既定の Bug Finder 解析では、現在の解析境界の外部からの特定の入力に関する "汚染されたサイズでのメモリの割り当て" 問題にフラグを設定しない場合があります。Polyspace 解析での汚染のソースを参照してください。Polyspace 解析の現在のスコープ以外から発生したすべてのデータを汚染されたものと見なすには、コマンド ライン オプション [-consider-analysis-perimeter-as-trust-boundary] を使用します。

すべて展開する

問題

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

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

リスク

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

修正方法

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

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

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

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

多くの場合、結果の詳細には欠陥につながる一連のイベントが表示されます。そのシーケンス内のどのイベントについても修正を実装できます。結果の詳細にイベント履歴が表示されない場合は、ソース コード内で右クリック オプションを使用して逆のトレースを行い、これまでの関連するイベントを確認できます。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 は割り当てられたメモリ ブロックの外を指しますが、もうデリファレンスされることはありません。

問題

汚染されたサイズでのメモリの割り当てでは、callocmalloc のようなメモリ割り当て関数を、セキュリティで保護されないソース由来のサイズ引数についてチェックします。

リスク

メモリ割り当てが制御されていないと、プログラムによってシステム メモリが過剰に要求されることがあります。その結果、メモリ不足の状態やリソースの過剰割り当てによりクラッシュすることがあります。

修正方法

メモリを割り当てる前に引数の値をチェックして、範囲を超えないことを確認します。

例 — 入力引数を使用したメモリの割り当て
#include<stdio.h>
#include <stdlib.h>

int* bug_taintedmemoryallocsize(void) {
    size_t size;
    scanf("%zu", &size);
    int* p = (int*)malloc(size); //Noncompliant
    return p;
}

この例では、malloc がポインター psize 量のメモリを割り当てます。size はプログラムのユーザーが指定するため、その値はチェックされません。サイズが使用可能なメモリの量よりも大きい場合、プログラムはクラッシュする可能性があります。

修正 — 割り当てられるメモリのサイズをチェック

1 つの修正方法として、malloc 操作を実行する前に、割り当てるメモリのサイズをチェックします。この例では、サイズが正であり最大サイズより小さいことを確認するためにチェックしています。


#include "stdlib.h"

enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};

int* corrected_taintedmemoryallocsize(int size) {
    int* p = NULL;
          /* Fix: Check entry range before use */
    if (size>0 && size<SIZE128) {
        p = (int*)malloc((unsigned int)size);
    }
    return p;
}
問題

割り当てられたメモリがターゲット オブジェクトを保持するのに十分ではない場合に、"メモリの割り当て不足" が発生します。次のコードについて考えます。

#include <stdlib.h>

typedef struct S {
    int a;
    int b;
    int c;
}S;

void foo(){
	S* a;
	a = (S*)malloc(sizeof(S*)*2);//Noncompliant	
}
void bar(){
	S* a;
	a = (S*)malloc(sizeof(S*)*3); //Compliant	
}
S 型のオブジェクトは 3 つの整数を格納する必要があり、通常 12 バイトのメモリを必要とします。foo() では、S 型のオブジェクトに対して割り当てられるメモリのサイズは 8 バイトです。割り当てられたメモリがターゲット オブジェクトを保持するのに十分ではないため、Polyspace® はこのルールに対する違反を報告します。bar() では、割り当てられるメモリが 12 バイトであり、十分なサイズです。Polyspace は違反を報告しません。

リスク

メモリ割り当て不足が原因で、バッファー オーバーフローと予期しないプログラムの終了が発生します。

修正方法

メモリ ブロックを割り当てるときに、十分なメモリを割り当てます。

例 — malloc() 呼び出しによるオブジェクトのメモリの割り当て

この例では、関数 foo() がポインター ab に対してメモリを割り当てます。

  • a にメモリを割り当てるときの sizeof() の引数は S* であり、これはポインターです。64 ビット システムでは、すべてのポインター型のサイズは 8 バイトです。malloc() ステートメントは 24 バイトを割り当てます。これは、S 型のオブジェクトを格納できる十分なサイズです。割り当てられたメモリでオブジェクト a を保持できるので、Polyspace は違反を報告しません。このステートメントが原因で、sizeof において使用された誤った型の欠陥が発生します。

    malloc() ステートメントのコンテキストから、オブジェクト a は、S 型のオブジェクトの 3 要素配列であると想定されます。malloc() ステートメントではこの配列全体に対して十分なメモリが割り当てられないことは明らかです。for ループで配列の要素にアクセスすると、メモリが不足しているためにこのルールに対する違反となります。このように、a を配列として使用すると、malloc() ステートメントの sizeof において使用された誤った型の欠陥が原因で違反となります。a を配列ではなくオブジェクトとして使用する場合、このルールに違反しません。

  • b に対するメモリの割り当てでは、malloc() ステートメントは 2 つの整数を格納するのに十分なメモリを割り当てます。S 型のオブジェクトには、3 つの整数を格納するのに十分なメモリが必要であるため、割り当てられたメモリでは不十分です。Polyspace はこのルールに対する違反を報告します。


#include <stdlib.h>


typedef struct S
{
  int a;
  int b;
  int c;
} S;

void setS (S * a, int x, int y, int z)
{
  a->a = x;			//Noncompliant: pointer access out of bounds
  a->b = y;
  a->c = z;
}

void foo ()
{
  S *a, *b;
  a = (S *) malloc (3 * sizeof (S *));	//Compliant but causes PTR_SIZEOF_MISMATCH defect/
  setS(a,22,32,45); //Compliant
  for (int i = 1; i < 3; ++i)
    {
      setS (a + i, i, i * i, i * i * i);

    }
  b = (S *) malloc (2 * sizeof (int));	//Noncompliant 

}
修正 — 十分なメモリの割り当て

この問題を修正するには、十分なメモリを割り当てます。ベスト プラクティスは、正しい引数型と、必要なメモリの量を計算するための正しい係数を指定した関数 sizeof() を使用することです。


#include <stdlib.h>


typedef struct S
{
  int a;
  int b;
  int c;
} S;

void setS (S * a, int x, int y, int z)
{
  a->a = x;			//Compliant: pointer access out of bounds
  a->b = y;
  a->c = z;
}

void foo ()
{
  S *a, *b;
  a = (S *) malloc (3 * sizeof (S));	//No violations of PTR_SIZEOF_MISMATCH*/
  setS(a,22,32,45); //Compliant
  for (int i = 1; i < 3; ++i)
    {
      setS (a + i, i, i * i, i * i * i);

    }
  b = (S *) malloc (1 * sizeof (S));	//Compliant 
  //...
  free(a);
  free (b);
}

チェック情報

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