メインコンテンツ

CERT C: Rule ENV34-C

Do not store pointers returned by certain functions

説明

ルール定義

特定の関数によって返されるポインターを保存しないようにします。1

Polyspace 実装

ルール チェッカーは、"再呼び出し不可能な標準関数の戻り値の不適切な使用" をチェックします。

すべて展開する

問題

再呼び出し不可能な標準関数の戻り値の不適切な使用は、次の事象がこの順番で起きる場合に発生します。

  1. getenvsetlocale など、再呼び出し不可能な標準関数から返されるバッファーをポイントします。

    user = getenv("USER");
  2. 再呼び出し不可能な標準関数をもう一度呼び出します。

    user2 = getenv("USER2");
  3. 最初のステップ以降バッファーが変更されずに残っていると想定して、最初のステップのポインターを使用またはデリファレンスします。しかし、バッファーは 2 番目のステップの呼び出しで変更されています。

    次に例を示します。

    var=*user;

場合によっては、関数 getenv を 2 回呼び出さなくてもポインターを返すだけで、欠陥が発生することがあります。次に例を示します。

char* func() {
     user=getenv("USER");
     .
     .
     return user;
}

この欠陥の対象となる関数の詳細は、再呼び出し不可能な標準関数のドキュメンテーションを参照してください。

リスク

C 標準では、getenv などの再呼び出し不可能な関数が "静的" バッファーへのポインターを返すことを許可しています。バッファーが静的であるため、getenv の 2 回目の呼び出しはバッファーを変更します。最初の呼び出しで返されたポインターを 2 回目の呼び出しの後も使用し続けると、予期しない結果になる可能性があります。ポイントされるバッファーは、最初の呼び出しの値ではなくなります。

関数 getenv を 2 度呼び出さなくてもポインターを返すだけで、この欠陥が発生します。それは、関数の呼び出し側が、getenv の 2 回目の呼び出しの "後で" 返されたポインターを使用することになるためです。getenv の呼び出しからポインターを返すことによって、関数は安全に使用できなくなります。

この欠陥の対象となる他の再呼び出し不可能な関数についても、同じ理由が当てはまります。

修正方法

getenv の最初の呼び出し後に、返されたポインターが指すバッファーをコピーします。getenv の 2 回目の呼び出し後は、このコピーを使用します。2 回目の呼び出しがバッファーを変更する場合でも、コピーには影響しません。

例 - getenv の 2 回目の呼び出し後に getenv の戻り値を使用
#include <stdlib.h>
#include <string.h>

int func()
{
    int result = 0;

    char *home = getenv("HOME");   /* First call */ 
    if (home != NULL) {
        char *user = NULL;
        char *user_name_from_home = strrchr(home, '/');
 
        if (user_name_from_home != NULL) {
            user = getenv("USER");   /* Second call */
            if ((user != NULL) &&
                (strcmp(user, user_name_from_home) == 0))  //Noncompliant
            {
                result = 1;
            }
        }
    }
    return result;
}

この例では、ポインター user_name_from_home はポインター home から派生しています。homegetenv の最初の呼び出しから返されるバッファーを指しています。そのため、user_name_from_home は同じバッファー内の場所を指します。

このバッファーは、getenv を再度呼び出した後に変更されます。user_name_from_home を引き続き使用すると、予期しない結果が発生する可能性があります。

修正 — 2 回目の呼び出し前にバッファーのコピーを作成

getenv の最初の呼び出しからのバッファーを 2 回目の呼び出し以降も使用する場合は、最初の呼び出し後にバッファーをコピーします。1 つの修正方法として、関数 strdup を使用してコピーします。

#include <stdlib.h>
#include <string.h>

int func()
{
    int result = 0;

    char *home = getenv("HOME");    
    if (home != NULL) {
        char *user = NULL;
        char *user_name_from_home = strrchr(home, '/'); 
        if (user_name_from_home != NULL) {
            /* Make copy before second call */
            char *saved_user_name_from_home = strdup(user_name_from_home); 
            if (saved_user_name_from_home != NULL) {
                user = getenv("USER");  
                if ((user != NULL) &&
                    (strcmp(user, saved_user_name_from_home) == 0)) 
                {
                    result = 1;
                }
                free(saved_user_name_from_home);
            }
        }
    }
    return result;
}

チェック情報

グループ: Rule 10.環境 (ENV)

バージョン履歴

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.