メインコンテンツ

va_start に渡された不正な型のデータ

va_start マクロの 2 番目の引数のデータ型による未定義の動作の発生

説明

この欠陥は va_start マクロの第 2 引数が以下のいずれかのデータ型である場合に発生します。

  • 既定の引数プロモーションが行われている際に変化するデータ型。

    たとえば、char および shortint または unsigned int にプロモーションされ、floatdouble にプロモーションされるとします。int 型と double 型は既定の引数プロモーションの際に変化しません。

  • (C のみ) レジスタ型または register 修飾子で宣言されているデータ型。

  • (C++ のみ) 参照データ型。

  • (C++ のみ) 非トリビアル コピー コンストラクターまたは自明でない移動コンストラクターをもつデータ型。

リスク

次のような可変個引数関数 (可変個の引数をもつ関数) を考えます。

void multipleArgumentFunction(int someArg, short rightmostFixedArg, ...) {
    va_list myList;
    va_start(myList, rightmostFixedArg);
    ...
    va_end(myList);
}
va_start マクロは、可変個引数関数の固定パラメーターの後にある追加引数をリスト内で取得できるように可変引数リストを初期化します。C11 および C++14 規格によると、フラグが設定されたいずれかのデータ型を va_start マクロの 2 番目の引数 (前の例では rightmostFixedArg) で使用する場合、動作は未定義です。

データ型が非トリビアル コピー コンストラクターに関連している場合、動作は処理系定義になります。たとえば、コピー コンストラクターが va_start の呼び出しの中で呼び出されるかどうかは、コンパイラによって異なります。

修正方法

va_start マクロを使用する際、可変個引数関数の右端の名前付きパラメーターに対して int 型、unsigned int 型、または double 型の使用を試みます。その後、このパラメーターを va_start マクロの 2 番目の引数として使用します。

たとえば、次の例では、可変個引数関数の右端の名前付きパラメーターのデータ型は、サポートされているデータ型 int です。

void multipleArgumentFunction(int someArg, int rightmostFixedArg, ...) {
    va_list myList;
    va_start(myList, rightmostFixedArg);
    ...
    va_end(myList);
}

未定義の動作や処理系定義の動作を回避するには、可変個引数関数の使用を最小限にします。可変個引数関数の使用を検出するには、MISRA C:2012 Rule 17.1 または MISRA C++:2008 Rule 8-4-1 のチェッカーを使用します。

すべて展開する

#include <string>
#include <cstdarg>

double addVariableNumberOfDoubles(double* weight, short num, ...) {
    double sum=0.0;
    va_list list;
    va_start(list, num);
    for(int i=0; i < num; i++) {
        sum+=weight[i]*va_arg(list, double);
    }
    va_end(list);
    return sum;
}

double addVariableNumberOfFloats(float* weight, int num, std::string s, ...) {
    float sum=0.0;
    va_list list;
    va_start(list, s);
    for(int i=0; i < num; i++) {
        sum+=weight[i]*va_arg(list, float);
    }
    va_end(list);
    return sum;
}

この例で、チェッカーは以下の va_start の呼び出しにフラグを設定します。

  • addVariableNumberOfDoubles: 引数の型が short であり、この引数に対して int への既定の引数プロモーションが行われるため。

  • addVariableNumberOfFloats: 引数の型が std::string であり、非トリビアル コピー コンストラクターがあるため。

修正 — va_start の 2 番目の引数のデータ型を修正

va_start マクロの 2 番目の引数がサポートされているデータ型であることを確認します。次の例では以下の点が修正されています。

  • addVariableNumberOfDoubles で、可変個引数関数の最後の名前付きパラメーターのデータ型を int に変更。

  • addVariableNumberOfFloats で、最後の名前付きパラメーターのデータ型が int になるように、可変個引数関数の 2 番目と 3 番目のパラメーターを入れ替え。

#include <string>
#include <cstdarg>

double addVariableNumberOfDoubles(double* weight, int num, ...) {
    double sum=0.0;
    va_list list;
    va_start(list, num);
    for(int i=0; i < num; i++) {
        sum+=weight[i]*va_arg(list, double);
    }
    va_end(list);
    return sum;
}

double addVariableNumberOfFloats(double* weight, std::string s, int num, ...) {
    double sum=0.0;
    va_list list;
    va_start(list, num);
    for(int i=0; i < num; i++) {
        sum+=weight[i]*va_arg(list, double);
    }
    va_end(list);
    return sum;
}

結果情報

グループ: プログラミング
言語: C | C++
既定値: 手書きコードはオン、生成コードはオフ
コマンド ライン構文: VA_START_INCORRECT_TYPE
影響度: Medium

バージョン履歴

R2019a で導入