メインコンテンツ

ISO/IEC TS 17961 [addrescape]

Escaping of the address of an automatic object

説明

ルール定義

自動オブジェクトのアドレスのエスケープ。1

Polyspace 実装

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

  • スタック変数へのポインターまたは参照が範囲外

  • putenv ファミリ関数の引数としての自動変数の使用

すべて展開する

問題

スタック変数へのポインターまたは参照が範囲外は、ローカル変数へのポインターまたは参照が変数のスコープを逸脱している場合に発生します。次に例を示します。

  • 関数が、ローカル変数を指すポインターを返す。

  • 関数が代入 globPtr = &locVar を実行する。globPtr はグローバル ポインター変数、locVar はローカル変数です。

  • 関数が代入 *paramPtr = &locVar を実行する。paramPtr は関数パラメーター (つまり int** ポインターなど)、locVar はローカルの int 変数です。

  • C++ メソッドが代入 memPtr = &locVar を実行する。memPtr はメソッドが属するクラスのポインター データ メンバー、locVar はメソッドから見てローカルな変数です。

欠陥は、関数 alloca を使用して割り当てたメモリにも適用されます。この欠陥は静的なローカル変数には適用されません。Polyspace® は、関数定義に含まれるローカル オブジェクトは同じスコープ内にあると仮定します。

リスク

ローカル変数にはスタック上のアドレスが割り当てられます。ローカル変数のスコープがいったん終了すると、このアドレスは再利用可能になります。このアドレスを使用して変数のスコープ外にあるローカル変数値にアクセスすると、予期しない動作を引き起こす可能性があります。

ローカル変数を指すポインターが変数のスコープを逸脱していると、Polyspace Bug Finder™ によってその欠陥が強調表示されます。この欠陥は、ポインターに格納されているアドレスが使用されていない場合でも発生します。コードを保守可能なものにするため、ポインターが変数のスコープを逸脱しないようにすることをお勧めします。ポインター内のアドレスが現在使用されていない場合でも、関数の他の使用者がそのアドレスを使用することで動作が未定義となる可能性があります。

修正方法

ローカル変数へのポインターまたは参照が変数スコープを逸脱しないようにします。

例 - ローカル変数を指すポインターが関数から返される
void func2(int *ptr) {
    *ptr = 0;
}

int* func1(void) {
    int ret = 0;
    return &ret ;
}
void main(void) {
    int* ptr = func1() ;
    func2(ptr) ;
}

この例では、func1 はローカル変数 ret を指すポインターを返します。

main では、ptr はローカル変数のアドレスを指します。ret のスコープは func1 に制限されているため、func2 内で ptr がアクセスされると、そのアクセスは無効になります。

問題

putenv ファミリ関数の引数としての自動変数の使用は、putenv ファミリ関数の引数が自動の存続期間をもつローカル変数である場合に発生します。

リスク

関数 putenv(char *string) は、引数のコピーを作成するのではなく、指定された引数を指すポインターを環境配列に挿入します。引数が自動変数の場合、putenv() 呼び出しを含む関数から値が返された後、メモリが上書きされる可能性があります。その後、別の関数が getenv() を呼び出した場合、適切にデリファレンスできないスコープ外の変数のアドレスが返されます。このスコープ外の変数は、環境変数が予期しない値を取る、プログラムが応答を停止する、任意のコード実行を可能にする脆弱性を生むなどの原因になる可能性があります。

修正方法

環境変数の設定/設定解除に setenv()/unsetenv() を使用します。または、putenv ファミリ関数の引数と動的に割り当てられたメモリを使用するか、アプリケーションに再入要件がない場合は引数と静的存続期間を使用します。たとえば、再帰や割り込みのないシングルスレッド実行には再入は必要ありません。実行中の呼び出し (再入) はできません。

例 - putenv() の引数としての自動変数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZE1024 1024

void func(int var)
{
    char env[SIZE1024];
    int retval = sprintf(env, "TEST=%s", var ? "1" : "0");
    if (retval <= 0) {
        /* Handle error */
    }
	/* Environment variable TEST is set using putenv().
	The argument passed to putenv is an automatic variable. */
    retval = putenv(env);   
    if (retval != 0) {
        /* Handle error */
    }
}
              

この例では、sprintf() は文字列 TEST=varenv に格納します。その後、環境変数 TEST の値が putenv() を使用して var に設定されます。env は自動変数であるので、TEST の値は func() が値を返すときに変化する可能性があります。

修正 — putenv() の引数に static 変数を使用

env を静的存続期間の変数として宣言します。env のメモリ位置は、func() が値を返した後でも、プログラムの存続期間中は上書きされません。

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

#define SIZE1024 1024 
void func(int var)
{
	/* static duration variable */
    static char env[SIZE1024]; 
    int retval = sprintf(env,"TEST=%s", var ? "1" : "0");
    if (retval <= 0) {
        /* Handle error */
    }
	
	/* Environment variable TEST is set using putenv() */
    retval=putenv(env);   
	if (retval != 0) {
        /* Handle error */
    }
}
修正 — setenv() を使用して環境変数値を設定

TEST の値を var に設定するために、setenv() を使用します。

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

#define SIZE1024 1024 

void func(int var)
{
	/* Environment variable TEST is set using setenv() */
    int retval = setenv("TEST", var ? "1" : "0", 1); 
	
    if (retval != 0) {
        /* Handle error */
    }
}

チェック情報

決定可能性:決定不可能

バージョン履歴

R2019a で導入

すべて展開する


1 Extracts from the standard "ISO/IEC TS 17961 Technical Specification - 2013-11-15" are reproduced with the agreement of AFNOR. Only the original and complete text of the standard, as published by AFNOR Editions - accessible via the website www.boutique.afnor.org - has normative value.