メインコンテンツ

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

CWE Rule 253

Incorrect Check of Function Return Value

R2023a 以降

説明

ルールの説明

The software incorrectly checks a return value from a function, which prevents the software from detecting errors or exceptional conditions.

Polyspace 実装

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

  • errno がリセットされていません

  • 保護されていない動的メモリ割り当て

すべて展開する

問題

この問題は、エラー状態を示すように errno を設定する関数を呼び出す前に、errno をリセットしなかった場合に発生します。ただし、関数呼び出し後には、errno でエラー状態をチェックしています。

リスク

errno はクリーンではなく、前の呼び出しの値を保持している可能性があります。この errno でエラーをチェックすると、エラーが発生したという誤った印象を与える可能性があります。

errno はプログラム起動時にゼロに設定されますが、その後 C 標準ライブラリ関数では errno がリセットされません。必要に応じて、errno にゼロを明示的に設定しなければなりません。

修正方法

エラー状態を示すために errno を設定する関数を呼び出す前に、errno を明示的にゼロにリセットします。

例 — strtod を呼び出す前に errno をリセットしていない
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <float.h>

#define fatal_error() abort()

double func(const char *s1, const char *s2)
{
    double f1;
    f1 = strtod (s1, NULL);  //Noncompliant    
    if (0 == errno) {  
      double f2 = strtod (s2, NULL); 
        if (0 == errno) {        
            long double result = (long double)f1 + f2;
            if ((result <= (long double)DBL_MAX) && (result >= (long double)-DBL_MAX)) 
				  {
                return (double)result;
            }
        }
    }
    fatal_error();
    return 0.0;
}

この例では、strtod 呼び出しの前に errno をゼロにリセットしていません。この errno がゼロかどうかをチェックすると、誤検知につながる可能性があります。

修正 — 呼び出し前に errno をリセット

1 つの修正方法として、strtod を呼び出す前に errno をゼロにリセットします。

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

#define fatal_error() abort()

double func(const char *s1, const char *s2)
{
    double f1;
    errno = 0;                   
    f1 = strtod (s1, NULL);
    if (0 == errno) {            
      double f2 = strtod (s2, NULL);  
        if (0 == errno) {       
            long double result = (long double)f1 + f2;
            if ((result <= (long double)DBL_MAX) && (result >= (long double)-DBL_MAX)) 
  			{
                return (double)result;
            }
        }
    }
    fatal_error();
    return 0.0;
}
問題

この問題は、前回のメモリ割り当てが成功したかどうかを初めにチェックせずに、動的に割り当てられたメモリにアクセスしたときに発生します。

リスク

malloccalloc または realloc を使用してメモリを動的に割り当てる際、要求されたメモリが使用可能でない場合は値 NULL が返されます。割り当ての後、コードでこの NULL 値をチェックせずにメモリ ブロックにアクセスした場合、このアクセスの成功は保証されません。

修正方法

割り当てられたメモリ位置にアクセスする前に、malloccalloc、または realloc の戻り値が NULL かどうかをチェックします。

int *ptr = malloc(size * sizeof(int));

if(ptr) /* Check for NULL */ 
{
   /* Memory access through ptr */
}

例 — 保護されていない動的メモリ割り当てエラー
#include <stdlib.h>

void Assign_Value(void) 
{
  int* p = (int*)calloc(5, sizeof(int));

  *p = 2;  //Noncompliant
  /* Defect: p is not checked for NULL value */

  free(p);
}

メモリ割り当てが失敗した場合、関数 callocNULLp に返します。p を使用してメモリにアクセスする前に、コードでは pNULL かどうかのチェックを行っていません。

修正 — null 値をチェック

1 つの修正方法として、デリファレンスの前に p の値が NULL かどうかをチェックすることができます。

#include <stdlib.h>

void Assign_Value(void)
 {
   int* p = (int*)calloc(5, sizeof(int));

   /* Fix: Check if p is NULL */
   if(p!=NULL) *p = 2; 

   free(p);
 }
例 — デリファレンス時のみの保護されていない動的メモリ割り当てエラー
#include <stdlib.h>
#include<string.h>
typedef struct recordType {
    const char* id;
    const char* data;
} RECORD;

RECORD* MakerecordType(const char *id,unsigned int size){
    RECORD *rec = (RECORD *)calloc(1, sizeof(RECORD));
    rec->id = strdup(id);  //Noncompliant

    const char *newData = (char *)calloc(1, size);
    rec->data = newData;
    return rec;
}

この例では、前回の動的メモリ割り当てからの NULL 値をチェックせずにポインター rec がデリファレンスされたときに、チェッカーが欠陥を報告します。

同様の問題がポインター newData でも発生します。ただし、ポインターはデリファレンスされませんが、rec->data にコピーされるだけなので、欠陥は報告されません。null の可能性があるポインターにコピーするだけでは問題になりません。たとえば、関数 recordType_new の呼び出し元がデリファレンスの前に rec->dataNULL 値をチェックすれば、null ポインターのデリファレンスは回避されます。

チェック情報

カテゴリ: Error Conditions, Return Values, Status Codes

バージョン履歴

R2023a で導入