DCL10-C. 可変引数関数の作成者と利用者の間の取り決めを維持する
可変引数関数は可変個の引数をとるが、問題になりやすい。可変引数関数は、その関数の作成者と利用者の間に暗黙の取り決めを定めるが、この取り決めによって各関数呼び出しで与えられた引数の数が分かるようになっている。この取り決めが強制されないと、未定義の動作を引き起こすかもしれない。C 標準の附属書 J 「未定義の動作」の 141 を参照すること。
引数の処理
以下に示すコードでは、可変引数関数 average()
は関数に渡された正の整数引数の平均値を計算する [Seacord 2013]。この関数は va_eol
の値(-1
)をとる引数に遭遇するまで、引数を処理し続ける。
enum { va_eol = -1 }; unsigned int average(int first, ...) { unsigned int count = 0; unsigned int sum = 0; int i = first; va_list args; va_start(args, first); while (i != va_eol) { sum += i; count++; i = va_arg(args, int); } va_end(args); return(count ? (sum / count) : 0); }
注意: va_start()
を呼び出して引数リストを初期化しなくてはならない。また、可変引数リストの処理が終わったら va_end()
を呼び出さなくてはならない。
違反コード
以下のコード例では、average()
関数を次のように呼び出している:
int avg = average(1, 4, 6, 4, 1);
引数の終了値である va_eol
の省略が意味するのは、この関数は、偶然 va_eol
の値に遭遇するかエラーが起きるまで、スタックから値を読み込み処理し続けるということである。
適合コード
以下の適合コードでは、va_eol
を最後の引数として追加することで、取り決めを守らせている。
int avg = average(1, 4, 6, 4, 1, va_eol);
違反コード
ほかによくある間違いとしては、与えられた引数以上の変換指定子を使うことである。以下にその例を示す。
const char *error_msg = "Resource not available to user."; /* ... */ printf("Error (%s): %s", error_msg);
このコードでは、関数が、存在しない引数を処理する結果となり、プロセスに関する情報が漏えいする可能性がある。
適合コード
以下の適合コードでは、書式指定子の数と引数の数が一致している。
const char *error_msg = "Resource not available to user."; /* ... */ printf("Error: %s", error_msg);
引数リストにご用心
可変プリミティブ va_list
を引数としてとる C 関数には更なるリスクが伴う。vfprintf()
, vfscanf()
, vprintf()
, vscanf
, vsnprintf()
, vsprintf()
および vsscanf()
関数呼び出しには va_arg()
マクロが使用され、パラメータ化された va_list
が無効にされる。したがって、これらの関数に一度 va_list
を渡したら、va_start()
呼び出しに続いて va_end
を呼び出さない限り va_list
は再利用できない。
リスク評価
可変引数関数を不適切に使用すると、プログラムが異常終了したり、意図せぬ情報漏えいにつながる恐れがある。
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
DCL10-C |
高 |
中 |
高 |
P6 |
L2 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
LDRA tool suite |
V. 8.5.4 |
41 S |
部分的に実装済み |
PRQA QA-C | 8.1 |
0185 |
部分的に実装済み |
関連するガイドライン
ISO/IEC TR 24772:2013 | Subprogram Signature Mismatch [OTR] |
MISRA-C | Rule 16.1 |
MITRE CWE | CWE-628, Function call with incorrectly specified arguments |
参考資料
[Seacord 2013] | Chapter 6, "Formatted Output" |
翻訳元
これは以下のページを翻訳したものです。
DCL10-C. Maintain the contract between the writer and caller of variadic functions (revision 86)