メインコンテンツ

CWE Rule 562

Return of Stack Variable Address

R2023a 以降

説明

ルールの説明

A function returns the address of a stack variable, which will cause unintended program behavior, typically in the form of a crash.

Polyspace 実装

ルール チェッカーは、"スタック変数へのポインターまたは参照が範囲外" をチェックします。

すべて展開する

問題

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

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

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

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

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

  • (C++11 以降) 関数が、参照によって関数のローカル変数を取得するラムダ式オブジェクトを返す。

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

リスク

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

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

修正方法

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

例 — ローカル変数を指すポインターが関数から返される

void func2(int *ptr) {
    *ptr = 0;
}

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

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

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

例 — ローカル変数を指すポインターがラムダ式によってエスケープされる

auto createAdder(int amountToAdd) {
  int addThis = amountToAdd;  //Noncompliant
  auto adder = [&] (int initialAmount) {
      return (initialAmount + addThis);
  };
  return adder;
}
 
void func() {
  auto AddByTwo = createAdder(2);
  int res = AddByTwo(10);
}

この例では、関数 createAdder で、参照によってローカル変数 addThis を取得するラムダ式 adder を定義しています。addThis のスコープは関数 createAdder に制限されます。createAdder によって返されたオブジェクトが呼び出されると、変数 addThis に対する参照がスコープ外からアクセスされます。この方法でアクセスが行われると、addThis の値は未定義となります。

修正 – 参照ではなくラムダ式でのコピーによってローカル変数を取得

関数がラムダ式オブジェクトを返す場合は、ラムダ式でローカル変数を参照によって取得しないようにします。代わりにコピーによって変数を取得します。

コピーによって取得された変数の有効期間はラムダ オブジェクトと同じです。しかし、参照によって取得された変数の有効期間は、多くの場合、ラムダ オブジェクト自体よりも短くなります。ラムダ オブジェクトを使用すると、スコープ外からアクセスされるこれらの変数は値が未定義となります。


auto createAdder(int amountToAdd) {
  int addThis = amountToAdd;
  auto adder = [=] (int initialAmount) {
      return (initialAmount + addThis);
  };
  return adder;
}
 
void func() {
  auto AddByTwo = createAdder(2);
  int res = AddByTwo(10);
}

チェック情報

カテゴリ: Bad Coding Practices

バージョン履歴

R2023a で導入