JPCERT コーディネーションセンター

EXP10-C. 部分式の評価順序や副作用の発生順序に依存しない

EXP10-C. 部分式の評価順序や副作用の発生順序に依存しない

部分式の評価順序や副作用の発生順序の多くはC 標準では未規定の動作とされている。直観に反し、未規定の動作とは、言語標準は2, 3の可能性を提供するだけで、そのいずれが選択されねばならないかという点についてそれ以上の要求はしない。それゆえ、未規定の動作は、異なる処理系が異なる選択をできるがゆえ、移植性の問題になりうる。しかしながら、動的スケジューリングが採用されている場合には、プロセスの生存期間を通じて固定した実行シーケンスが存在しないかもしれない。異なる順序で実行され得る演算は実際、異なる順序で実行されるかもしれない。

C 標準のセクション 6.5 には次のように記載されている [ISO/IEC 9899:2011]。

後で規定する部分を除いて、副作用と部分式の値の計算について順序は規定されない。

以下に、部分式の評価順序や副作用の発生順序が未規定である状況の例を示す。

このレコメンデーションは「EXP30-C. 副作用完了点間の評価の順序に依存しない」に関連しているが、移植性のないあるいは混乱させる可能性のあるふるまいに着目したものである。

違反コード

関数指示子 (function designator) 、実引数、および実引数中の部分式、これらの評価順序は未規定である。しかし、実際の関数呼出しの前に副作用完了点が存在する。たとえば、次の関数呼出し

(*pf[f1()]) (f2(), f3() + f4())

関数 f1(), f2(), f3() および f4() はどんな順序で呼び出されるかわからない。しかし、すべての副作用は、pf[f1()] が参照する関数が呼び出される前に完了していなければならない。

したがって、以下のコードは未規定の動作に依存している。

#include <stdio.h>

int g;

int f(int i) {
  g = i;
  return i;
}

int main(void) {
  int x = f(1) + f(2);
  printf("g = %d\n", g);
  /* ... */
  return 0;
}

このコードは、値としてg1 が代入されるかもしれないし、2が代入されるかもしれない。

適合コード

以下の適合コードはオペランドの評価順序に依存しておらず、一意に解釈することができる。

#include <stdio.h>

int g;

int f(int i) {
  g = i;
  return i;
}

int main(void) {
  int x = f(1); 
  x += f(2);
  printf("g = %d\n", g);
  /* ... */
  return 0;
}

このコードではgには常に2が代入される。

例外

EXP10-EX1: && および || 演算子は左から右へ評価することを保証している。最初のオペランドの評価後に副作用完了点が存在する。

EXP10-EX2: 条件演算子 ?:ではまず第一オペランドが評価され、その評価後に副作用完了点が存在する。第二オペランドが評価されるのは、第一オペランドが0でない場合のみである。第三オペランドが評価されるのは、第一オペランドが0と等しい場合のみである。

EXP10-EX3: 関数呼出しの前には副作用完了点が存在する。つまり、関数指示子、実引数、実引数中の部分式が評価されるのは、関数が呼び出される前であるということである。

EXP10-EX4: コンマ演算子の左オペランドは、右オペランドが評価される前に評価される。この間に副作用完了点が存在する。

関数呼出しにおける複数の引数を区切るためにもコンマは使われるが、このようなコンマは「コンマ演算子」ではない。関数呼出し時の複数の引数はどのような順序で評価してもよく、各引数の評価の間に副作用完了点は存在しない。

リスク評価

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

EXP10-C

P8

L2

自動検出(最新の情報はこちら

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

以下のパターンを探すことで、このレコメンデーションの違反を検出できる。

    • ある副作用完了点から次の副作用完了点までの間に二つの関数を呼び出すすべての式
    • これら二つの関数がどちらも同じ静的変数を変更する
    • この静的変数の値が、式の後のコードから参照される

Coverity

6.5

EVALUATION_ORDER

コンパイラのフラグごとに、またはコンパイラまたはプラットフォームごとに文の動作が異なることがあるため、文に未定義の評価順序による同じ値に対する複数の副作用がある例を検出できる。

LDRA tool suite

V. 8.5.4

35 D
72 D
74 D
1 Q
134 S

実装済み

PRQA QA-C 8.1 3226 部分的に実装済み

動的メモリが二つの関数に渡される場合もこのレコメンデーションの違反となりうるが、静的解析で検出することは極めて困難であろう。

関連するガイドライン
CERT C++ Secure Coding Standard EXP10-CPP. Do not depend on the order of evaluation of subexpressions or the order in which side effects take place
ISO/IEC TR 24772:2013 Operator Precedence/Order of Evaluation [JCW]
Side-effects and Order of Evaluation [SAM]
MISRA-C Rule 12.2
参考資料
[ISO/IEC 9899:2011] Section 6.5, "Expressions"
翻訳元

これは以下のページを翻訳したものです。

EXP10-C. Do not depend on the order of evaluation of subexpressions or the order in which side effects take place (revision 54)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter